将其转换为通用存储库模式

时间:2013-04-05 22:34:59

标签: asp.net-mvc-3 entity-framework repository-pattern unit-of-work

我已经开始将项目转换为通用存储库和工作单元模式。到目前为止,我已经能够将控制器中的所有直接上下文引用反向工程到通用存储库;但是,我遇到以下两行代码的问题:

`context.Entry(ticket).Collection(i => i.TicketItems).Load();
            ticket.TicketItems.Clear();`

这是我的控制器在删除TicketTicketItem之间的任何引用之前所做的事情。 TicketTicketItem之间存在多对多关系。因此,从TicketItems

中删除所有Ticket之前我使用的是这两行代码

1 个答案:

答案 0 :(得分:5)

您可以在存储库界面中使用两种方法 - 一种用于导航参考,另一种用于导航集合:

public interface IRepository<T>
{
    void LoadNavigationReference<TReference>(T entity,
        Expression<Func<T, TReference>> navigationProperty,
        params Expression<Func<TReference, object>>[] includes)
        where TReference : class;

    void LoadNavigationCollection<TElement>(T entity,
        Expression<Func<T, ICollection<TElement>>> navigationProperty,
        params Expression<Func<TElement, object>>[] includes)
        where TElement : class;
}

它们应该支持包含其他嵌套导航属性。实施将是:

public class Repository<T> : IRepository<T>
    where T : class
{
    private readonly MyContext _dbContext;

    public Repository(MyContext dbContext)
    {
        _dbContext = dbContext;
    }

    public void LoadNavigationReference<TReference>(T entity,
        Expression<Func<T, TReference>> navigationProperty,
        params Expression<Func<TReference, object>>[] includes)
        where TReference : class
    {
        if (includes == null || includes.Length == 0)
            _dbContext.Entry(entity).Reference(navigationProperty).Load();
        else
            _dbContext.Entry(entity).Reference(navigationProperty).Query()
                .IncludeMultiple(includes).Load();
    }

    public void LoadNavigationCollection<TElement>(T entity,
        Expression<Func<T, ICollection<TElement>>> navigationProperty,
        params Expression<Func<TElement, object>>[] includes)
        where TElement : class
    {
        if (includes == null || includes.Length == 0)
            _dbContext.Entry(entity).Collection(navigationProperty).Load();
        else
            _dbContext.Entry(entity).Collection(navigationProperty).Query()
                .IncludeMultiple(includes).Load();
    }
}

上面使用的IncludeMultiple扩展方法取自Ladislav Mrnka's answer here

您问题中的示例将如下所示:

repository.LoadNavigationCollection(ticket, i => i.TicketItems);
ticket.TicketItems.Clear();

其中repository的类型为IRepository<Ticket>

如果TicketItem有另一个导航属性,例如TicketItemDetails,您可以用TicketItems这样急切地加载它:

repository.LoadNavigationCollection(ticket, i => i.TicketItems,
    t => t.TicketItemDetails);

修改

BTW作为关于通用存储库的重要注意事项:上面是一个通用存储库的一部分,它实际上有16个方法,并且在我停止扩展它并完全放弃这种风格之前我已经在项目的早期阶段使用过。

存储库在开始时有大约5种方法(就像你在互联网上看到的大多数常用存储库一样)。在不失去很多实体框架功能的情况下,不可能只使用这5种方法。因此,我需要逐步扩展它,由项目中的实际需求驱动,并且在我从项目中删除它之前它永远不会变得“完整”。

问题是:如果你要向某人展示界面(“这里我有一个超级通用和技术独立的数据访问界面”),他会马上说“啊哈,你正在使用实体框架!”。原因是几乎每个方法都只是实体框架方法的包装器,并且您无法通过使用其他名称来隐藏接口方法。整个界面都有EF DbContext / Code-First。

现在,尝试使用除Entity Framework之外的其他技术实现该接口。很可能你会遇到我遇到的同样的问题:缺少很多方法来利用其他技术的力量,或者现有的方法有错误的参数,或者有太多的方法你无法合理地实现其他方法技术

我甚至失败了,为构建单元测试接口的内存实现失去了所有乐趣。

在我看来,这样的通用存储库是Leaky Abstraction的典型示例,其中您想到的真实实现在整个界面中闪耀。

但如果你不能抽象出实体框架的用法,那么建立一个通用的存储库接口是没有意义的。