有没有更好的方法来编写通用的懒惰DbContext查询?

时间:2014-01-07 22:34:09

标签: c# asp.net-mvc entity-framework dependency-injection

到目前为止我得到的是:

public DbSet GetQueryableLazy<TEntity>()
        where TEntity : class, IContextEntity
    {
        context.Configuration.LazyLoadingEnabled = false;
        return context.Set<TEntity>();
    }

我可以在控制器动作中调用它:

IQueryable<Person> people = (IQueryable<Person>)repository.GetQueriableLazy<Person>()
                                  .Include("Addresses");

其中存储库是控制器中依赖注入的结果。但似乎上面的代码违背了依赖注入的全部目的,因为我在控制器中依赖于DbContext进行操作(因为GetQueryableLazy返回DbSet),如果稍后我将从实体框架切换到NHibernate,我将不得不替换:

(IQueryable<Person>)repository.GetQueriableLazy<Person>()
                                  .Include("Addresses");

到处!

我想要实现的是,如果我可以创建附加到“DbContext.include()”所有“参数”的泛型方法并根据这些附件返回查询,那么结果将如下所示:

 public IQueryable<TEntity> GetQueryableLazy<TEntity>(params IContextEntity[] contextEntities)
    where TEntity : class, IContextEntity
{
    context.Configuration.LazyLoadingEnabled = false;
    return context.Set<TEntity>().Include(contextEntities);
}

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

这是漏洞的抽象故事。每个IQueryable实现都有自己的增强功能,超越了接口的“抽象”。

每个成熟的ORM都有一个概念,即在查询结果中包含导航属性(急切加载),以防止延迟加载,这将触发N + 1个查询。差异已经从语言开始了。实体框架讨论“包含”,NHibernate关于“获取”,其他实现关于“扩展”。除了这个微不足道的差异之外,很难隐藏真实的实现差异与消费层。

让我们通过努力根据表达式参数化预测加载行为来说明这一点。

实体框架(EF)有几种Include方法,最有趣的两种方法是:

  1. DbQuery.Include(string path)
  2. QueryableExtensions.Include<T, TProperty> (Expression<Func<T, TProperty>> path)
  3. 因此,如果你想抽象出来,你有两个选择:

    1)IQueryable<T> GetQueryable<T>(params string[] includes)
    2)IQueryable<T> GetQueryable<T>(params Expression<Func<T, object>>[] includeExpressions)

    好的,让我们继续2)。现在看NHibernate。使用NHibernate的LINQ API,您可以编写

    // snippet 1
    var customers = session.Query<Customer>()
        .FetchMany(c => c.Orders)
        .ThenFetchMany(o => o.OrderLines)
    

    使用EF,这将是

    // snippet 2
    var customers = context.Set<Customer>()
        .Include(c => c.Orders.Select(o => o.OrderLines))
    

    所以使用EF作为DAL你可以使用方法2.使用NHibernate ...你不能。代码段1中的表达式具有不同的类型,因此params参数无法输入它们。

    所以尝试第三种方法,一种流畅的API,这样我们就可以输入不同类型的表达式了?我们说

    3)IJoinableQueryable<T> GetQueryable<T>(Expression<Func<T, TProperty>> includeExpression)

    返回IJoinableQueryable<T, TProperty>(想不出更好的名字)。此接口应实现IQueryable<T> 提供一种方法,根据TProperty 添加下一个“包含”或“获取”来执行同样适用于<T>。这意味着为了取悦NHibernate,当使用EF时,你会让事情变得不必要。即便如此,您迟早会遇到导致不同行为的实施细节,或者根据DAL排除包含的特定组合。

    结论

    不要尝试抽象DAL实现的特定功能,以使DAL可插入。可插拔的DAL是一种错觉(我们现在只涉及一个方面)。但是,如果您未在服务层中公开IQueryable,则可以从应用程序的其余部分隐藏大部分DAL功能。一个结果是您使用规范而不是表达式来参数化服务方法。

    更多相关信息:http://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/