6

I am trying to create a multiple include method in my repository to use as follows:

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

I tried something as:

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 

But I get the error:

Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Data.Entity.DbSet'.

Note that the original include is the following:

public static IQueryable Include( this IQueryable source, Expression> path ) where T : class;

Can I make this work without turning my repository method into static?

Thank You,

Miguel

1
  • You only have to do _context.Set<T>().AsQueryable(). Commented Dec 11, 2012 at 19:28

3 Answers 3

9

If you really want to create your own .Include non-extension method that allows for multiple paths in one call, internally translating to the already provided .Include method, you can do something like

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; } 

This is pretty close to what you have already, but avoids the pitfall with Enumerable.Aggregate that you encountered: you'd have the same problem if you replace IQueryable<T> query with var query in my version.

Note that using many .Include may harm performance. If it does in your situation, you can rewrite it to use multiple queries, which you can run one after the other in a transaction.

Personally, as you can call the already provided .Include extension method (using System.Data.Entity;) on any IQueryable, I'd just write it as:

repository.Posts.Include(x => x.Images).Include(x => x.Tags).First(x => x.Id == 1) 
Sign up to request clarification or add additional context in comments.

11 Comments

Why do you say that many Include may harm preformance? I turned off Lazy Loading so sometimes I need to use Include. And of course I will include only the entities I need. I can include Tags and not Images if I don't need Images. Am I missing something from your words?
The reason why I am not using repository.Posts.Include(x => x.Images).Include(x => x.Tags) is because I am using a generic repository. Of course your approach is also a good one. In fact I have use it in the past. I just opted , in this case, for using a Generic repository.
@Shapper a.Include(b).Include(c).Include(d).Include(e) is translated to an SQL select * from a left join b on b... = a... left join c on c... = a... left join d... = a... left join e... = a... Let's say a has one record, b, c, d and e each have 100. So you want to load 401 records into memory. EF will however have to filter through 100.000.000 records in the SQL result set in order to get the 401 entities.
@Shapper In such a case, even if lazy loading is turned off, you can still use EntityCollection.Load, which does not have that problem.
Oh, I forgot to mention, EF sometimes does manage to come up with complicated queries that give less rows, but they still perform a lot worse than simpler separate queries :)
|
0
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(); } } 

1 Comment

okay..earlier I was getting the count and the result in one query.But that doesn't work when lazy loading is off. Since the I am projecting, the includes don't get executed. After removing the group by it worked.
0

I guess the shortest way is as follows:

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; } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.