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

SOf*_*tic 2 entity-framework unit-of-work repository-pattern asp.net-mvc-3

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

`context.Entry(ticket).Collection(i => i.TicketItems).Load();
            ticket.TicketItems.Clear();`
Run Code Online (Sandbox Code Playgroud)

这是我的控制器在删除a Ticket和a 之间的任何引用之前所做的事情TicketItem.之间存在许多一对多的关系TicketTicketItem.所以这些代码两行是什么,我在使用前删除所有TicketItems来自Ticket

Sla*_*uma 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

IncludeMultiple上面使用的扩展方法取自Ladislav Mrnka的答案.

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

repository.LoadNavigationCollection(ticket, i => i.TicketItems);
ticket.TicketItems.Clear();
Run Code Online (Sandbox Code Playgroud)

哪里repository是类型IRepository<Ticket>.

如果TicketItem有另一个导航属性,比如说TicketItemDetails,你可以用TicketItems这种方式急切地加载它:

repository.LoadNavigationCollection(ticket, i => i.TicketItems,
    t => t.TicketItemDetails);
Run Code Online (Sandbox Code Playgroud)

编辑

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

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

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

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

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

在我看来,这样的通用存储库是一个漏洞抽象的典型例子,你真正的实现在整个界面中闪耀.

但是,如果您无法抽象出实体框架的使用,那么构建通用存储库接口就毫无意义.

  • @Luis:我不建议直接在控制器中使用EF和`context`.关注点分离是一种很好的做法,但不需要总是抽象.我今天所做的是更多关注业务逻辑.在您的示例中,我可能有一种"TicketService"类或接口,其方法为`ClearTicket`,我将调用`ticketService.ClearTicket(ticket)`.但是在这种方法中我直接使用EF而没有回购.它将整个业务逻辑移出控制器,而不仅仅是数据访问.整个主题部分是品味问题,你必须找到自己的最佳实践:) (2认同)