将其转换为通用存储库模式 [英] translate this into a generic repository pattern
问题描述
我已经开始将项目翻译成通用存储库和工作单元。到目前为止,我已经能够将控制器中的所有直接上下文引用反向工程到通用存储库中;但是,我遇到了以下两行代码:
`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 $以上使用的c $ c>扩展方法取自 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屋!