【问题标题】:How to use Repository Interface that uses Generics with Dependency Injection?如何使用使用泛型和依赖注入的存储库接口?
【发布时间】:2013-01-16 18:54:55
【问题描述】:

我正在尝试使用以下通用存储库接口进行 DI 和构造函数注入:

public interface IRepository<TEntity> : IDisposable where TEntity : class

问题是为了定义接口的实例,我必须提供这样的类类型:

private IRepository<Person> _personRepository;

问题是如果我使用 DI(并且我使用 Unity for IoC 框架),那么我必须在构造函数中定义多个实例以获取我需要使用的所有存储库接口,如下所示:

public MyClass(IRepository<Person> personRepository,
               IRepository<Orders> ordersRepository,
               IRepository<Items> itemsRepository,
               IRepository<Locations> locationsRepository)
{
  _personRepository = personRepository;
  _OrdersRepository = ordersRepository; 
  _itemsRepository = itemsRepository;
  _locationsRepository = locationsRepository;
}

问题:

  1. 这样可以吗?
  2. 如果不是,我在哪里迷失了这个概念?
  3. 即使这是正确的,Unity 将接口注册到具体类型有什么意义?我已经这样做了,因为通用存储库强迫我声明。

请帮我解决这个问题,感谢您的所有帮助!

【问题讨论】:

    标签: c# dependency-injection unity-container ioc-container


    【解决方案1】:

    正如 D Stanley 所指出的,依赖项必须是具体的接口。否则,你要在哪里声明 T?你的依赖类可能是通用的,但在某些时候你仍然必须说“T 是一个人”。

    也就是说,Unity 可以很好地处理注册泛型类型。

    假设您使用一个通用类Repository&lt;T&gt; 来实现IRepository&lt;T&gt;,该类包装了一个DbSet&lt;T&gt;(或其他)。

    随后将进行以下注册和解析(包括注入任何构造函数):

    container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
    
    // no specific registration needed for the specific type resolves
    container.Resolve(<IRepository<Person>);
    container.Resolve(<IRepository<Order>); 
    

    如果您需要特定类型的覆盖(说 Items 存储库出于某种原因是特殊的,因此它具有完全实现的 ItemRepository 类),只需在通用实现之后注册该特定实现:

    container.RegisterType<IRepository<Item>, ItemRepository>();
    

    解析IRespository&lt;Item&gt;现在会得到你的具体实现。

    为了记录,我认为这只能在代码中完成,而不是配置文件。有人可以随意纠正这个假设。

    【讨论】:

    • 您可以在 Unity 配置文件中注册泛型类。我已经做了。 &lt;register type="IRepository[[Item]]" mapTo="ItemRepository" /&gt; 就是一个例子。爱我一些 Unity。
    【解决方案2】:

    这样好吗?

    当然。个人偏好是像你一样使用构造函数注入还是属性注入。构造函数注入更简洁,因为您不必为构造函数提供大量参数,但它也更安全。

    Unity 将接口注册到具体类型有什么意义

    一个原因是您可以对MyClass 进行单元测试,而不必使用访问数据库的实际存储库。您可以“伪造”存储库以返回硬编码值以进行测试。

    【讨论】:

    • 我希望我能做的是将IRepository&lt;T&gt; repository 作为方法参数,但我知道这行不通。你能解释为什么它不会吗?把所有这些都传进去感觉不对,但也许这是正确的,我想多了。
    • 因为每个存储库都是不同的类型,而 MyClass (显然)需要 4 个不同的存储库。您必须单独提供具体类。以您的方式注入多个依赖项并没有错(或不常见)。
    • 但是我什么时候需要额外的方法呢?
    【解决方案3】:

    您可以考虑使用将其他几个服务捆绑在一起的Aggregate Service,但您还应该仔细查看您的MyClass 是否为trying to do too much;拥有大量依赖项可能表明这一点。

    【讨论】:

    【解决方案4】:

    这似乎没问题,只是缺少一点;您需要 UnitOfWork 模式来启用事务。 如果没有应用 UnitOfWork 模式,所有存储库都会尝试在不同的上下文中提交数据库操作。

    【讨论】:

    • 他的问题中没有任何内容表明他需要 UoW。正如其他人指出的那样,也许他的班级做得太多,但他从未说过尝试一次在所有存储库上提交修改。此外,从技术上讲,不同的存储库不一定使用不同的上下文。
    【解决方案5】:

    我需要使用配置文件来做到这一点。这实际上很容易,但我花了一段时间才弄清楚。

    在这个例子中,我们有一个接口IRepository&lt;T&gt;,它有两个实现:

    1. OneRepository
    2. TwoRepository

    然后我们有一个名为Worker 的类,它依赖于IRepository&lt;One&gt; 和IRepository&lt;Two&gt;。我们要求 unity 为我们创建一个 Worker 的实例,并从配置文件中找出依赖关系。

    接口和实现

    在本例中,所有这些都在命名空间 ConsoleApplication1 中。

    public class Worker
    {
        private readonly IRepository<One> one;
        private readonly IRepository<Two> two;
    
        public Worker(IRepository<One> one, IRepository<Two> two)
        {
            this.one = one;
            this.two = two;
        }
    
        public string DoOne()
        {
            return this.one.Add(new One());
        }
    
        public string DoTwo()
        {
            return this.two.Add(new Two());
        }
    }
    
    public interface IRepository<T>
    {
        string Add(T t);
    }
    
    public class OneRepository : IRepository<One>
    {
        public string Add(One t)
        {
            return "One";
        }
    }
    
    public class TwoRepository : IRepository<Two>
    {
        public string Add(Two t)
        {
            return "Two";
        }
    }
    
    public class One { }
    public class Two { }
    

    统一配置

    请注意我们指示unity并告诉它程序集的名称。然后我们注册这 2 个实现。

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
      </configSections>
      <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <assembly name="ConsoleApplication1" />
        <container>
          <register type="ConsoleApplication1.IRepository[[ConsoleApplication1.One]]" mapTo="ConsoleApplication1.OneRepository" />
          <register type="ConsoleApplication1.IRepository[[ConsoleApplication1.Two]]" mapTo="ConsoleApplication1.TwoRepository" />
        </container>
      </unity>
    </configuration>
    

    应用程序

    这是Composition Root。

    public class Program
    {
        static void Main()
        {
            UnityContainer container = new UnityContainer();
            var res = container.LoadConfiguration();
            Worker worker = res.Resolve<Worker>();
            Console.WriteLine(worker.DoOne());
            Console.WriteLine(worker.DoTwo());
            Console.Read();
        }
    }
    

    预期的输出是:

    One
    Two
    

    【讨论】:

      猜你喜欢
      • 2012-03-15
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-12
      相关资源
      最近更新 更多