Entity Framework 5. Несколько включений. Это возможно?

Я пытаюсь создать метод множественного включения в моем репозитории для использования следующим образом:

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'.

Обратите внимание, что исходный текст включает следующее:

public static IQueryable Include ( этот источник IQueryable, Выражение > путь ), где T: class;

Могу ли я сделать эту работу, не превращая мой метод репозитория в статический?

Спасибо,

Мигель

Ответ 1

Если вы действительно хотите создать свой собственный метод .Include non-extension, который позволяет использовать несколько путей в одном вызове, внутренне переведя на уже предоставленный метод .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<T> query на var query в моей версии.

Обратите внимание, что использование многих .Include может нанести вред производительности. Если это происходит в вашей ситуации, вы можете переписать его для использования нескольких запросов, которые вы можете запускать один за другим в транзакции.

Лично, поскольку вы можете вызывать уже предоставленный метод расширения .Include (using System.Data.Entity;) на любом IQueryable, я просто напишу его как:

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

Ответ 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;
    }
}