EF Including Other Entities (Generic Repository pattern)

63,704

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();
    }
Share:
63,704

Related videos on Youtube

Kassem
Author by

Kassem

Updated on July 05, 2022

Comments

  • Kassem
    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
      gideon about 13 years
      elaborate 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
      Kassem about 13 years
      @giddy: Check out the update above. Thank you.
  • Kassem
    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
    Ladislav Mrnka about 13 years
    Don't use version with strings. EF 4.1 also offers strongly typed version with lambdas.
  • Kassem
    Kassem about 13 years
    @Ladislav Mrnka: Ok but how? Could you provide an example please?
  • Ladislav Mrnka
    Ladislav Mrnka about 13 years
    I 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
    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
    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
    Kassem about 13 years
    One question though, shouldn't the calling code be: var query = context.Customers.IncludeMultiple<Customer>(c => c.Address, c => c.Orders... etc)?
  • Ladislav Mrnka
    Ladislav Mrnka about 13 years
    This 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
    Slauma about 13 years
    Ouh, that's good! (Ladislav, I think he meant that you forgot to write ".IncludeMultiple(..." behind "context.Customers" in your example.)
  • Kassem
    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
    Shimmy Weitzhandler about 13 years
    I downloaded it with NuGet, that was just fun!
  • StuartLC
    StuartLC over 12 years
    Brilliant! - 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
    Jeff Borden over 11 years
    How do you implement IncludeMultiple to extend DbContext?
  • Jeff Borden
    Jeff Borden over 11 years
    Understood, 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
    Jeff Borden over 11 years
    Apologies, I just needed to learn how to extend IQueryable... ty for the solution!
  • Ladislav Mrnka
    Ladislav Mrnka over 11 years
    @JeffBorden: IncludeMany is extension method (it is static and the first parameter uses this keyword) it works with all classes implementing IQueryable. That includes DbSet.
  • Bennett Dill
    Bennett Dill almost 10 years
    It 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
    liqSTAR about 3 years
    Could 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
    Bibin Gangadharan about 3 years
    Yes 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
    Bellash about 2 years
    And how to you chain with .ThenInclude(s=>s.Something)?