将其转换为通用存储库模式 [英] translate this into a generic repository pattern

查看:129
本文介绍了将其转换为通用存储库模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

$中删除所有的 TicketItems b $ b

解决方案

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

  public interface IRepository< T> 
{
void LoadNavigationReference< TReference>(T entity,
Expression< Func< T,TReference>> navigationProperty,
params Expression&FunC< TReference,object> ] include)
其中TReference:class;

void LoadNavigationCollection< TElement>(T entity,
Expression< Func< T,ICollection< TElement>>>>导航属性
params Expression&FunC< TElement,object> > [] include)
其中TElement:class;
}

他们应该支持包括其他嵌套的导航属性。实现将是:

  public class Repository< T> :IRepository< T> 
其中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,对象>> [] include)
其中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,
表达式< Func< T,ICollection< TElement>>>>>>导航属性
params Expression& Func< TElement,object>> [] include)
其中TElement:class
{
if(includes == null || includes.Length == 0)
_dbContext。条目(实体).Collection(navigationProperty).Load();
else
_dbContext.Entry(entity).Collection(navigationProperty).Query()
.IncludeMultiple(includes).Load();
}
}

IncludeMultiple 扩展方法取自 Ladislav Mrnka的回答此处



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

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

其中存储库的类型为 IRepository< Ticket>



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

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

修改



BTW是关于通用存储库的关键方面的注意事项:以上是一个通用存储库的一部分,其实际上有16种方法,而且我已经在项目的早期阶段使用,在我停止扩展之前,完全放弃了这种风格。 / p>

开始时,存储库有大约5种方法(像在互联网上看到的大多数通常的存储库)。只有这5种方法才能实现,而不会失去实体框架的大量功能。所以,我需要按照项目的实际要求逐步扩展它,并且在将其从项目中删除之前从未变得完整。



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



现在,尝试用另一种技术比实体框架。很可能你会碰到与我所遇到的同样的问题:许多方法缺少利用其他技术的力量,或现有的方法有错误的参数,或者有太多的方法,你不能合理地实现与其他技术。



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



在我看来,这样的通用存储库是泄漏抽象的典型示例在实际的实现中,您所想到的真正的实现通过整个界面发光。



但是,如果不能将Entity Framework的用法抽象出来,构建通用存储库接口就是无意义。


I have started translating a project into a generic repository and unit of work pattern. So far I have been able to reverse engineer all direct context references in the controllers to a generic repository; however, I am having trouble with the following two lines of codes:

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

This is what my controller was doing before to remove any reference between a Ticket and a TicketItem. There is a many-to-many relation between Ticket and TicketItem. So those two lines of code is what I was using before to remove all TicketItems from a Ticket

解决方案

You could have two methods in the repository interface - one for navigation references and one for navigation collections:

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

They are supposed to support including other nested navigation properties as well. The implementation would be:

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

The IncludeMultiple extension method used above is taken from Ladislav Mrnka's answer here.

The example in your question would then look like this:

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

where repository is of type IRepository<Ticket>.

If TicketItem had another navigation property, say TicketItemDetails, you could eagerly load it together with the TicketItems this way:

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

Edit

BTW as critical side note about Generic Repositories: The above is part of a generic repository that has actually 16 methods and that I have used in an early stage of a project before I have stopped to extend it and abandoned this style completely.

The repository had around 5 methods in the beginning (like most of the usual repositories you see on the internet). It was impossible to work with only those 5 methods without losing a lot of Entity Framework's power. So, I needed to extend it step by step, driven by actual requirements in the project, and it never became "complete" before I removed it from the project.

The problem is: If you would show the interface to someone ("here I have a super generic and technology independent data access interface") he would say immediately "aha, you are using Entity Framework!". The reason is that almost every method is just a wrapper around an Entity Framework method and you can't hide that by using other names for the interface methods. The whole interface smells of EF DbContext/Code-First.

Now, try to implement that interface with another technology than Entity Framework. Most likely you'll run into the same problem that I had: Lots of methods are missing to leverage the power of that other technology, or the existing methods have wrong parameters or there are too many methods you can't reasonably implement with the other technology.

I even failed and lost all fun to build an in-memory implementation of that interface for unit testing.

In my opinion such a Generic Repository is a typical example of a Leaky Abstraction where the real implementation you have in mind shines through the whole interface.

But if you can't abstract the usage of Entity Framework away, building a generic repository interface is rather pointless.

这篇关于将其转换为通用存储库模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆