DI 和存储库模式

Posted

技术标签:

【中文标题】DI 和存储库模式【英文标题】:DI and repository pattern 【发布时间】:2016-04-26 05:57:11 【问题描述】:

目前,我的代码与此类似(只是为了说明一点而缩短):

DAL

存储库接口

public interface IRepository<TEntity, in TKey>

    IList<TEntity> GetAll();
    TEntity Get(TKey id);
    TEntity Add(TEntity item);
    TEntity Update(TEntity item);
    bool Remove(TKey id);

基础 EF 存储库

public class BaseEFRepository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity: class, IEntity<TKey> where TKey: struct

    protected readonly DbContext _dbContext;

    public BaseRepository()
    
        _dbContext = new MyDB();
        _dbContext.Configuration.ProxyCreationEnabled = false;
        _dbContext.Configuration.LazyLoadingEnabled = false;
    

    public virtual TEntity Get(TKey id)
    
        return _dbContext.Set<TEntity>().Find(id);
    

    public virtual IList<TEntity> GetAll()
    
        return _dbContext.Set<TEntity>()
            .ToList();
    

    public virtual TEntity Add(TEntity item)
    
        _dbContext.Set<TEntity>().Add(item);
        _dbContext.SaveChanges();
        return item;
    
    .....
    .....

基础存储库的示例实现

public interface IContactsRepository : IRepository<Contact, long>

    Contact GetByEmployeeId(string empId, ContactType type);
    IList<Contact> GetByEmployeeId(string empId);


public class ContactsRepository : BaseEFRepository<Contact, long>, IContactsRepository

    public Contact GetByEmployeeId(string empId, ContactType type)
    
        var contact = _dbContext.Set<Contact>()
            .FirstOrDefault(d => d.EmployeeId == empId && d.ContactType == type);

        return contact;
    

    public IList<Contact> GetByEmployeeId(string empId)
    
        var contacts = _dbContext.Set<Contact>()
            .Where(d => d.EmployeeId == empId)
            .ToList();

        return contacts;
    

BLL

public class Contacts

    public Contact Get(long id)
    
        IContactsRepository repo = ResolveRepository<IContactsRepository>();
        var contact = repo.Get(id);
        return contact;
    

    public Contact GetByEmployeeId(string empId, ContactType type)
    
        IContactsRepository repo = ResolveRepository<IContactsRepository>();         
        return repo.GetByEmployeeId(empId, type);
    
    .......
    .......

现在,一切都很好。我可以简单地做这样的事情:

 var _contacts = new Contacts();
 var contact = _contacts.GetByEmployeeId("C1112", ContactType.Emergency);

当我阅读this blog post 时开始感到困惑,作者说使用如下代码:

IContactsRepository repo = ResolveRepository<IContactsRepository>();  

是一种糟糕的技术,它是反模式的,应该将所有内容都注入代码的根部。我看不出如何使用存储库模式来做到这一点。我正在使用 WCF 使用它。那么,我到底如何从 WCF 的第一次调用中注入所有内容?我无法得到它。我在这里错过了什么?

最后一件事,在这种情况下,WCF 是最后一层,它应该只知道它之前的层,即 BLL 层。如果我要按照该博客的作者的建议实现任何东西,我会让 WCF 层意识到 DAL 层,这不是不好的做法吗?如果我错了,请纠正我。

【问题讨论】:

那么,您的应用程序是 WCF 应用程序吗?它是托管在 IIS 中还是自托管?在控制台应用程序中?还是在 Windows 服务中? 【参考方案1】:

您需要使用构造函数注入,然后在Composition Root 中组合您的对象。

当你使用构造函数注入时,你通过构造函数注入依赖,所以你的类看起来像这样:

public class BaseRepository

    protected readonly DbContext _dbContext;

    //...
    public BaseRepository(DbContext context)
    
        _dbContext = context;
    
    //...


public class ContactsRepository : BaseEFRepository<Contact, long>, IContactsRepository

    //...
    public ContactsRepository(DbContext context)
        :base(context)
    

    
    //...


public class Contacts

    private readonly IContactsRepository m_ContactsRepository;

    public Contacts(IContactsRepository contacts_repository)
    
        m_ContactsRepository = contacts_repository;
    

    public Contact Get(long id)
    
        var contact = m_ContactsRepository.Get(id);
        return contact;
    
    //...

然后在合成根中,您将所有对象合成在一起。可选择通过 DI 容器。

这是一个使用Pure DI的组合示例:

var context = new MyDB();

context.Configuration.ProxyCreationEnabled = false;

context.Configuration.LazyLoadingEnabled = false;

var contacts = new Contacts(new ContactsRepository(context));

在 IIS 中托管的 WCF 应用程序中,组合根是自定义的ServiceHostFactory。 This answer 提供了有关如何执行此操作的更多详细信息。

【讨论】:

请原谅我的无知,但这不会让 WCF 层知道 DAL 层,而它应该只知道 BLL 层......如果我错了,请纠正我。 WCF 托管代码必须了解所有内容,是的。它是组合根。但是,服务层(例如 Contacts 类)不需要知道所有内容。 您应该将 WCF 托管逻辑视为系统的一个单独部分,并将其称为“组合根”。这与包含 WCF 服务本身的服务层完全不同。 Yacoub,好的,现在开始有意义了。我将深入研究这一点。不是程序员(我)并试图正确编程是一种痛苦。我应该改变我的爱好:/【参考方案2】:

@Matthew 的帖子应该可以回答您的问题。但是,我想提一下我在您的代码中注意到的与该问题相关的一件事。您不应该尝试手动解决依赖关系,但它应该由容器注入。我已修改您的代码以显示此行为:

public class Contacts

    private IContactsRepository repo;
    public Contacts(IContactsRepository injectedContactsRepository)
    
        repo = injectedContactsRepository;
    


    public Contact Get(long id)
    
        var contact = repo.Get(id);
        return contact;
    

//and other methods definitions...

【讨论】:

【参考方案3】:

您需要确定适合用作合成根的接缝。

对于 WCF,您需要发挥创意 - 您必须创建一个自定义 ServiceHostFactory 来截取正确的位置来构成您的对象根。

请参阅 Mark Seemann(“.Net 中的依赖注入”的作者 - 我认为您会觉得非常有用的一本书)的 this article 和 this article。

许多免费的 DI 容器 such as Autofac 为 WCF 提供现成的支持,这可能是最好的方法。

我真的无法高度推荐 Seemann 的书 - 它涉及到很多细节。

您可能还会发现this article on using Autofac DI with ASP.Net and a repository 很有趣。

【讨论】:

请原谅我的无知,但这不会让 WCF 层知道 DAL 层,而它应该只知道 BLL 层......如果我错了,请纠正我。 @HeidelBerGensis WCF 层是用作组合根的接缝,其中驻留了一些知道多个层的代码。这没关系,因为它只是连接 DI 容器的地方。

以上是关于DI 和存储库模式的主要内容,如果未能解决你的问题,请参考以下文章

如何将DI存储库转换为Type-class?

如何正确实施存储库适配器?

存储库模式 - 如何正确处理 JOIN 和复杂查询?

DAO 和存储库模式有啥区别?

具有多个后端/DI 时的 ORM 和 POCO - 架构?自动映射器?

存储库模式的好处和 Spring 实现