所有
我要求在存储库后面隐藏我的EF实现。我的简单问题:有没有办法在DbSet和DbSet.Local中执行'find'而不必同时处理它们。
例如 - 我有Add / Update / Remove / FindById的标准存储库实现。我通过添加FindByName方法来破坏通用模式(仅用于演示目的:)。这给了我以下代码:
客户端应用:
ProductCategoryRepository categoryRepository = new ProductCategoryRepository();
categoryRepository.Add(new ProductCategory { Name = "N" });
var category1 = categoryRepository.FindByName("N");
实施
public ProductCategory FindByName(string s)
{
// Assume name is unique for demo
return _legoContext.Categories.Where(c => c.Name == s).SingleOrDefault();
}
在此示例中,category1为null。
但是,如果我将FindByName方法实现为:
public ProductCategory FindByName(string s)
{
var t = _legoContext.Categories.Local.Where(c => c.Name == s).SingleOrDefault();
if (t == null)
{
t = _legoContext.Categories.Where(c => c.Name == s).SingleOrDefault();
}
return t;
}
在这种情况下,当我查询新条目和仅在数据库中的条目时,我得到了我的期望。但这提出了一些我感到困惑的问题:
1)我假设(作为存储库的用户)找不到下面的cat2。但它被发现了,很重要的是cat2.Name是“Goober”。
ProductCategoryRepository categoryRepository = new ProductCategoryRepository();
var cat = categoryRepository.FindByName("Technic");
cat.Name = "Goober";
var cat2 = categoryRepository.FindByName("Technic");
2)我想从我的存储库返回一个通用的IQueryable。
将调用包装到存储库中的DbSet似乎需要做很多工作。通常,这意味着我搞砸了一些东西。我很欣赏任何见解。
答案 0 :(得分:0)
对于旧版本的EF,由于需要引用,您可能会出现非常复杂的情况。在这个版本中,我建议不要暴露IQueryable但ICollections或IList。这将在您的存储库中包含EF并创建一个良好的分离。
编辑:此外,通过发回ICollection IEnumerable或IList,您将限制并控制发送到数据库的查询。这还可以让您更轻松地微调和维护系统。通过暴露IQueriable,你会暴露自己在人们为查询添加更多内容时产生的副作用,.Take()或.Where ... .SelectMany,EF将看到这些添加并将生成sql以反映这些不受控制的查询。不限制查询可能导致查询从UI执行,从长远来看,这是更复杂的测试和维护问题。
因为存储库模式的要点是能够随意交换它们。 DbSets的细节应该完全隐藏。
我认为你走的路很好。我唯一能问自己的是:
上下文是否存在多久?如果没有,那么不要担心查询本地。已插入/已删除的对象只有在被调用后才能访问。
如果这是一个长期存在的上下文,你需要访问已删除和插入的对象,那么查询本地是一个好主意,但正如你所指出的,你可能会遇到困难。