2013-01-16 57 views
12

我试图使用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. 即使这是正确的,注册Interface到具体类型的Unity有什么意义?我已经做到了,因为通用的存储库迫使我声明。

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

回答

2

可以吗?

当然。对于是否使用构造函数注入或个人注入有个人偏好。构造函数注入更清洁,因为您不必为构造函数提供很多参数,但它也更安全。

什么是统一点接口注册到具体类型

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

+0

我希望我可以做的是有'IRepository repository'作为方法的参数,但我知道这是行不通的。你能解释为什么它不会吗?感觉错了,所有这些都通过了,但也许它是正确的,我是过度思考。 – atconway

+1

由于每个存储库都是不同的类型,MyClass(显然)需要4个不同的存储库。你必须分别提供具体的类。以某种方式注入多个依赖关系并不错(或不常见)。 –

+0

但是什么时候需要额外的方法呢? – krypru

2

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

+1

本特例中的综合服务称为[工作单元](http://martinfowler.com/eaaCatalog/unitOfWork.html)。 – Steven

2

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

+0

他的问题中没有任何一点指向要求UoW的他。正如其他人指出的,也许他的班级做得太多了,但他从未说过要试图一次对所有存储库进行修改。另外,在技术上,不同的存储库不一定使用不同的上下文。 – Tipx

9

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

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

假设你实施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>); 

如果你需要一个类型(具体覆盖说,项目库是专门为所以它不管是什么原因一个完全实现ItemRepository类),只需注册一个具体实施通用一个后:

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

解决IRespository<Item>现在会得到你的具体实施。

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

+1

您可以在Unity配置文件中注册通用类。我已经做了。 ''就是一个例子。爱我一些团结。 –

0

我需要使用配置文件来做到这一点。这其实很简单,但花了我一段时间才弄明白。

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

  1. OneRepository
  2. TwoRepository

然后我们有一个名为Worker类取决于IRepository<One>IRepository<Two>。我们要求统一为我们创建一个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> 

应用

这是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