如何在存储库中包装EF 4.1 DbContext?

时间:2011-05-27 03:03:27

标签: repository entity-framework-4.1 iqueryable

所有

我要求在存储库后面隐藏我的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似乎需要做很多工作。通常,这意味着我搞砸了一些东西。我很欣赏任何见解。

1 个答案:

答案 0 :(得分:0)

对于旧版本的EF,由于需要引用,您可能会出现非常复杂的情况。在这个版本中,我建议不要暴露IQueryable但ICollections或IList。这将在您的存储库中包含EF并创建一个良好的分离。

编辑:此外,通过发回ICollection IEnumerable或IList,您将限制并控制发送到数据库的查询。这还可以让您更轻松地微调和维护系统。通过暴露IQueriable,你会暴露自己在人们为查询添加更多内容时产生的副作用,.Take()或.Where ... .SelectMany,EF将看到这些添加并将生成sql以反映这些不受控制的查询。不限制查询可能导致查询从UI执行,从长远来看,这是更复杂的测试和维护问题。

因为存储库模式的要点是能够随意交换它们。 DbSets的细节应该完全隐藏。

我认为你走的路很好。我唯一能问自己的是:

上下文是否存在多久?如果没有,那么不要担心查询本地。已插入/已删除的对象只有在被调用后才能访问。

如果这是一个长期存在的上下文,你需要访问已删除和插入的对象,那么查询本地是一个好主意,但正如你所指出的,你可能会遇到困难。