EF Including Other Entities (Generic Repository pattern)
Solution 1
Use just the Include extension on IQueryable. It is available in EF 4.1 assembly. If you don't want to reference that assembly in your upper layers create wrapper extension method in your data access assembly.
Here you have example:
public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes)
where T : class
{
if (includes != null)
{
query = includes.Aggregate(query,
(current, include) => current.Include(include));
}
return query;
}
You will use it for example like:
var query = context.Customers
.IncludeMultiple(
c => c.Address,
c => c.Orders.Select(o => o.OrderItems));
This query will load all customers with their addresses and orders and every order will contain its order items.
Solution 2
Say goodbye to the hardcoded ObjectQuery(T).Include calls
If you're using EF > 4, then it's built in, check DbExtensions.Include on MSDN.
Solution 3
//I have included the bare minimum here. Below is how to use it.
IQueryable<File> xg= UnitOfWork.Files.GetAllLazyLoad(d => d.FileId == 1,
r => r.FileCategory);
//where r.FileCategory is a navigational property.
//Interface
namespace Msh.Intranet.Repository.GenericRepoPattern
{
public interface IRepository<T> where T:class
{
IQueryable<T> GetAllLazyLoad(Expression<Func<T, bool>> filter, params Expression<Func<T, object>>[] children);
}
}
namespace Msh.Intranet.Repository.GenericRepoPattern
{
/// <summary>
/// The EF-dependent, generic repository for data access
/// </summary>
/// <typeparam name="T">Type of entity for this Repository.</typeparam>
public class EFRepository<T> : IRepository<T> where T : class
{
public EFRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
protected DbContext DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual IQueryable<T> GetAllLazyLoad(Expression<Func<T, bool>> filter, params Expression<Func<T, object>>[] children)
{
children.ToList().ForEach(x=>DbSet.Include(x).Load());
return DbSet;
}
}
}
Solution 4
For entity framework create a list of strings to hold the includes. See the below example code
public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter,
int skip = 0,
int take = int.MaxValue,
Func<IQueryable<TObject>, IOrderedQueryable<TObject>> orderBy = null,
IList<string> incudes = null)
{
var _resetSet = filter != null ? DbSet.AsNoTracking().Where(filter).AsQueryable() : DbSet.AsNoTracking().AsQueryable();
if (incudes != null)
{
foreach (var incude in incudes)
{
_resetSet = _resetSet.Include(incude);
}
}
if (orderBy != null)
{
_resetSet = orderBy(_resetSet).AsQueryable();
}
_resetSet = skip == 0 ? _resetSet.Take(take) : _resetSet.Skip(skip).Take(take);
return _resetSet.AsQueryable();
}
For more detail see the below link http://bulletproofcoder.com/blog/using-include-in-a-generic-entity-framework-repository
In entity framework core we shall use IIncludableQueryable. See the below example code
public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter,
int skip = 0,
int take = int.MaxValue,
Func<IQueryable<TObject>, IOrderedQueryable<TObject>> orderBy = null,
Func<IQueryable<TObject>, IIncludableQueryable<TObject, object>> include = null)
{
var _resetSet = filter != null ? DbSet.AsNoTracking().Where(filter).AsQueryable() : DbSet.AsNoTracking().AsQueryable();
if (include != null)
{
_resetSet = include(_resetSet);
}
if (orderBy != null)
{
_resetSet = orderBy(_resetSet).AsQueryable();
}
_resetSet = skip == 0 ? _resetSet.Take(take) : _resetSet.Skip(skip).Take(take);
return _resetSet.AsQueryable();
}
Related videos on Youtube
Kassem
Updated on July 05, 2022Comments
-
Kassem almost 2 years
I am using the Generic Repository pattern on top of Entity Framework Code First. Everything was working fine until I needed to include more entities in a query. I got to include one entity successfully, but now I can't figure out how to include multiple entities. Check out what I've got so far:
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class { var entityName = GetEntityName<TEntity>(); return _objectContext.CreateQuery<TEntity>(entityName); } public IList<TEntity> GetQueryWithInclude<TEntity>(string toInclude) where TEntity : class { var entityName = GetEntityName<TEntity>(); return _objectContext.CreateQuery<TEntity>(entityName).Include(toInclude).ToList(); } private string GetEntityName<TEntity>() where TEntity : class { return string.Format("{0}.{1}", _objectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name)); }
What I tried to do but didn't work was pass in an array of strings into a function, then try to "append" the includes on top of the query. I was wondering what if I called the GetQueryWithInclude and passed an entity name (actually a navigation property) at a time to aggregate the results of the query, but I'm worried this might duplicate the results of the query on each call... What do you think would be the best way to get this to work?
Thanks in advance!
UPDATE:
Here's an example of what I'm trying to achieve:
public IQueryable GetQueryWithIncludes(string[] otherEntities) { var entityName = GetEntityName<TEntity>(); //now loop over the otherEntities array //and append Include extensions to the query //so inside the loop, something like: _objectContext.GetQuery<TEntity>(entityName).Include(otherEntities[index]); }
-
gideon about 13 yearselaborate on "include more entities in a query" ? Can you give an example of this? If you have an
ObjectContext
you should be able to query an objects/or related objects with LinqToEntities -
Kassem about 13 years@giddy: Check out the update above. Thank you.
-
-
Kassem about 13 years@Ladislav Mrnka: This is exactly what I'm trying to do. I'm trying to use the Include extension, but I want to create a generic method that accepts any number of navigational properties in an array of strings and then include those with queried entity. See my edit/update above.
-
Ladislav Mrnka about 13 yearsDon't use version with strings. EF 4.1 also offers strongly typed version with lambdas.
-
Kassem about 13 years@Ladislav Mrnka: Ok but how? Could you provide an example please?
-
Ladislav Mrnka about 13 yearsI will post sample later if you don't get solution from somebody else. I'm writting from iPhone at the moment so providing code samples is pretty hard.
-
Kassem about 13 years@Ladislav Mrnka: Alright, I'll be very thankful. I have to go to my class right now, so I'll make sure to check later on. Appreciated :)
-
Kassem about 13 years@Ladislav Mrnka: Awesome! This is much better than using magic strings actually! Well, that means I have to upgrade to EF4.1 in order to use it, right? But I have to upgrade either way because CTP5 is not production read and does not work in a medium-trust environment.
-
Kassem about 13 yearsOne question though, shouldn't the calling code be:
var query = context.Customers.IncludeMultiple<Customer>(c => c.Address, c => c.Orders... etc)
? -
Ladislav Mrnka about 13 yearsThis works in CTP5 as well. Not sure what are you asking for in the last question. You mean that
Select
should not be part of the include? It is the way how to inlcude multiple levels of hiearchy with collection navigatin properties - it is equivalent to "Orders.OrderItems" in magic string version. -
Slauma about 13 yearsOuh, that's good! (Ladislav, I think he meant that you forgot to write ".IncludeMultiple(..." behind "context.Customers" in your example.)
-
Kassem about 13 years@Ladislav Mrnka: Yup, I meant the .IncludeMultiple and not the Select. Thanks a lot man, I really appreciate it :)
-
Shimmy Weitzhandler about 13 yearsI downloaded it with NuGet, that was just fun!
-
StuartLC over 12 yearsBrilliant! - just remember to add ref to the C:\Program Files\Microsoft ADO.NET Entity Framework 4.1\Binaries\EntityFramework.dll and using System.Data.Entity;
-
Jeff Borden over 11 yearsHow do you implement IncludeMultiple to extend DbContext?
-
Jeff Borden over 11 yearsUnderstood, and I apologize for being dense. But by default the DbContext does not include the IncludeMany method. How do I go about implementing your method to start including the IncludeMany method? Thank you...
-
Jeff Borden over 11 yearsApologies, I just needed to learn how to extend IQueryable... ty for the solution!
-
Ladislav Mrnka over 11 years@JeffBorden:
IncludeMany
is extension method (it is static and the first parameter usesthis
keyword) it works with all classes implementingIQueryable
. That includesDbSet
. -
Bennett Dill almost 10 yearsIt looks like your example method GetAllLazyLoad is actually eagerly loading the data. It is my understanding that lazy load would be without the include, pulling the data the first time its accessed. What your method seems to show is using the include pulling the data when the object is created.
-
liqSTAR about 3 yearsCould you elaborate it a bit more. Is this a self written Filter method and using IIncludableQueryable? And how are you using it in the code?
-
Bibin Gangadharan about 3 yearsYes it is self return filter method. IIncludableQueryable is a interface provided by EF code. More details : docs.microsoft.com/en-us/dotnet/api/… Example code (I wrote for my project): var clientStores = await _unitOfWork.ClientStoreRepository.Filter(predicate, skip: skip, take: take, orderBy: c => c.OrderBy(a => a.StoreName), include: s => s.Include(l => l.Location).ThenInclude(la => la.LocationArea) .Include(c => c.Country)).ToListAsync();
-
Bellash about 2 yearsAnd how to you chain with
.ThenInclude(s=>s.Something)
?