【问题标题】:Entity Framework 5. Multiple Include. Is this possible?实体框架 5. 多重包含。这可能吗?
【发布时间】:2012-12-11 11:54:24
【问题描述】:

我正在尝试在我的存储库中创建一个多重包含方法以如下使用:

repository.Include<Post>(x => x.Images, x => x.Tags).First(x => x.Id == 1)

我尝试了以下方法:

public IQueryable<T> Include<T>(params Expression<Func<T, Object>>[] paths) where T : class {
  return paths.Aggregate(_context.Set<T>(), (x, path) => x.Include(path));
} // Include

但我得到了错误:

无法将类型“System.Linq.IQueryable”隐式转换为“System.Data.Entity.DbSet”。

请注意,原始包含如下:

公共静态 IQueryable 包含( 这个 IQueryable 源, 表达式>路径 ) 其中 T : 类;

我可以在不将存储库方法变为静态的情况下完成这项工作吗?

谢谢,

米格尔

【问题讨论】:

  • 你只需要_context.Set&lt;T&gt;().AsQueryable()

标签: entity-framework


【解决方案1】:

如果您真的想创建自己的 .Include 非扩展方法,允许在一次调用中使用多个路径,在内部转换为已经提供的 .Include 方法,您可以执行类似的操作

public IQueryable<T> Include<T>(params Expression<Func<T, object>>[] paths)
    where T : class
{
    IQueryable<T> query = _context.Set<T>();
    foreach (var path in paths)
        query = query.Include(path);
    return query;
}

这与您已经拥有的非常接近,但避免了您遇到的Enumerable.Aggregate 的陷阱:如果在我的版本中将IQueryable&lt;T&gt; query 替换为var query,您将遇到同样的问题。

请注意,使用多个.Include 可能会损害性能。如果在您的情况下是这样,您可以重写它以使用多个查询,您可以在事务中一个接一个地运行。

就个人而言,因为您可以在任何IQueryable 上调用已经提供的.Include 扩展方法(using System.Data.Entity;),所以我将其写为:

repository.Posts.Include(x => x.Images).Include(x => x.Tags).First(x => x.Id == 1)

【讨论】:

  • 你为什么说很多Include可能会损害性能?我关闭了延迟加载,所以有时我需要使用 Include。当然,我将只包括我需要的实体。如果我不需要图像,我可以包含标签而不是图像。我是不是从你的话中漏掉了什么?
  • 我不使用 repository.Posts.Include(x => x.Images).Include(x => x.Tags) 的原因是因为我使用的是通用存储库。当然,您的方法也是一种很好的方法。事实上,我过去曾使用过它。在这种情况下,我只是选择了使用通用存储库。
  • @Shapper a.Include(b).Include(c).Include(d).Include(e) 被转换为 SQL select * from a left join b on b... = a... left join c on c... = a... left join d... = a... left join e... = a... 假设a 有一条记录,bcde 各有 100 条记录。所以您想将 401 条记录加载到内存中。然而,EF 必须过滤 SQL 结果集中的 100.000.000 条记录才能获得 401 个实体。
  • @Shapper 这种情况下,即使关闭了延迟加载,仍然可以使用EntityCollection.Load,不存在这个问题。
  • 哦,我忘了提一下,EF 有时确实会设法提出复杂的查询,这些查询会提供更少的行,但它们的性能仍然比简单的单独查询差很多 :)
【解决方案2】:
public ICollection<SomeClass> Filter(string name, int skip, int take,out int total, params Expression<Func<SomeClass, object>>[] includeProperties) {
      IQueryable<SomeClass> query = Session.All<SomeClass>().AsNoTracking();
      //query = includeProperties.Aggregate(query, (current, property) => current.Include(property));
      foreach (var property in includeProperties){
          query = query.Include(property);
      }

      if (!string.IsNullOrWhiteSpace(name)){
          query = query.Where(x => x.Name.Contains(name));
          var page = query.OrderBy(x => x.Name)
              .Skip(skip)
              .Take(take)
              .GroupBy(p => new{Total = query.Count()})
              .FirstOrDefault();
         total = (page != null) ? page.Key.Total : 0;
         if (page == null) {
            return new List<SomeClass>();
         }
         return  page.ToList();
      } else {
          var page = query.OrderBy(x => x.Name)
              .Skip(skip)
              .Take(take)
              .GroupBy(p => new { Total = query.Count() })
              .FirstOrDefault();
          total = (page != null) ? page.Key.Total : 0;
          if (page == null) {
             return new List<SomeClass>();
          }
          return page.ToList();
      }
  }

【讨论】:

  • 好的..早些时候我在一个查询中得到了计数和结果。但是当延迟加载关闭时这不起作用。由于我正在投影,因此包含不会被执行。删除该组后,它起作用了。
【解决方案3】:

我猜最短的方法如下:

public static class LinqExtensions
{
    /// <summary>
    /// Acts similar of .Include() LINQ method, but allows to include several object properties at once.
    /// </summary>
    public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] paths)
        where T : class
    {
        foreach (var path in paths)
            query = query.Include(path);

        return query;
    }
}

【讨论】:

    猜你喜欢
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多