【问题标题】:translate this into a generic repository pattern将其转换为通用存储库模式
【发布时间】:2013-03-28 12:08:05
【问题描述】:

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

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

这是我的控制器之前所做的,以删除 Ticket 和 TicketItem 之间的任何引用。 Ticket 和 TicketItem 之间存在多对多关系。所以这两行代码是我之前用来从Ticket中删除所有TicketItems的代码

【问题讨论】:

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


    【解决方案1】:

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

    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&lt;Ticket&gt;。

    如果TicketItem 有另一个导航属性,比如TicketItemDetails,您可以通过这种方式急切地将它与TicketItems 一起加载:

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

    编辑

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

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

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

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

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

    在我看来,这样的 Generic Repository 是 Leaky Abstraction 的典型示例,您想到的真正实现会在整个界面中闪耀。

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

    【讨论】:

    • 感谢您的回答和意见。我是整个通用存储库和工作单元模式的新手,但是根据您的经验,直接使用实体框架会更好吗?但是我将如何让我的控制器保持简短呢?您认为只要我不必创建那么多方法就可以坚持使用通用存储库吗?
    • 我喜欢您对存储库模式和 ef 的评论。只是不要这样做。存储库是从 2004 年开始的,或者当 orms 不那么流行的时候。原始存储库也只有查找、添加、删除、保存方法。
    • @Luis:我不建议直接在控制器中使用 EF 和 context。关注点分离是一种很好的做法,但并不总是需要抽象。我今天所做的更多是关注业务逻辑。在您的示例中,我可能有一种带有方法ClearTicket 的“TicketService”类或接口,我会调用ticketService.ClearTicket(ticket)。但在这种方法中,我直接使用 EF 而不使用 repo。它将整个业务逻辑移出控制器,而不仅仅是数据访问。整个主题部分是口味问题,您必须找到自己的最佳实践:)
    • @Peri Repository 是一个非常好的模式,它与 ORM 无关。 orm 是存储库的实现细节。话虽如此,在这种情况下,存储库是无用的,因为它暴露了 EF 的工作方式,即它只是一个泄漏的抽象。你可以在不接触界面的情况下切换 repo 的实现以使用 NOSQl db 或远程服务吗?
    • 在通用存储库上阅读您的 cmets 并大喊“是”!在每一个字。 (很高兴我一个人)。将其标记为表达良好的开发者体验。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    相关资源
    最近更新 更多