2

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

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

这是我的控制器之前所做的,以删除 aTicket和 a之间的任何引用TicketItemTicket和之间存在多对多的关系TicketItem。所以这两行代码是我之前用来TicketItemsTicket

4

1 回答 1

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

编辑

顺便说一句,作为关于通用存储库的关键旁注:以上是通用存储库的一部分,它实际上有 16 种方法,我在项目的早期阶段使用过,然后我停止扩展它并完全放弃这种风格。

该存储库一开始有大约 5 种方法(就像您在 Internet 上看到的大多数常用存储库一样)。仅使用这 5 种方法而不会失去很多 Entity Framework 的功能是不可能的。因此,我需要根据项目的实际需求逐步扩展它,并且在我将其从项目中删除之前,它从未变得“完整”。

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

现在,尝试使用实体框架以外的另一种技术来实现该接口。您很可能会遇到与我相同的问题:缺少很多方法来利用其他技术的强大功能,或者现有方法的参数错误,或者您无法合理地使用其他方法实现的方法太多技术。

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

在我看来,这样的通用存储库是泄漏抽象的典型示例,您想到的真正实现通过整个接口闪耀。

但是如果你不能抽象出实体框架的使用,那么构建一个通用的存储库接口是毫无意义的。

于 2013-04-05T23:00:52.677 回答