16

我正在尝试使用以下通用存储库接口进行 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 将接口注册到具体类型有什么意义?我已经这样做了,因为通用存储库迫使我声明。

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

4

5 回答 5

11

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

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

假设您使用包装 a (或其他)IRepository<T>的泛型类来实现。Repository<T>DbSet<T>

然后以下注册和解析将起作用(包括注入任何构造函数):

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>();

ResolvingIRespository<Item>现在会得到你的具体实现。

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

于 2013-01-17T01:22:49.360 回答
2

这个可以吗?

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

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

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

于 2013-01-16T19:00:05.843 回答
2

您可以考虑使用将其他几个服务捆绑在一起的聚合服务,但您也应该仔细查看您MyClass是否尝试做太多事情;拥有大量依赖项可能表明这一点。

于 2013-01-16T19:05:48.983 回答
2

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

于 2013-01-16T19:09:53.453 回答
0

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

在这个例子中,我们有一个接口IRepository<T>,它有 2 个实现:

  1. OneRepository
  2. TwoRepository

然后我们有一个名为的类Worker,它依赖于IRepository<One>and IRepository<Two>。我们要求 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 { }

统一配置

请注意我们指示统一并告诉它程序集的名称。然后我们注册这 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>

应用

这是组合根

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
于 2017-09-15T15:54:18.460 回答