Dynamic Include statements for eager loading in a query - EF 4.3.1

Go To StackoverFlow.com

10

I have this method:

public CampaignCreative GetCampaignCreativeById(int id)
        {
            using (var db = GetContext())
            {
                return db.CampaignCreatives
                    .Include("Placement")
                    .Include("CreativeType")                    
                    .Include("Campaign")
                    .Include("Campaign.Handshake")
                    .Include("Campaign.Handshake.Agency")
                    .Include("Campaign.Product")
                    .AsNoTracking()
                    .Where(x => x.Id.Equals(id)).FirstOrDefault();
            }
        }

I would like to make the list of Includes dynamic. I tried:

public CampaignCreative GetCampaignCreativeById(int id, string[] includes)
        {
            using (var db = GetContext())
            {
                var query = db.CampaignCreatives;

                foreach (string include in includes)
                {
                    query = query.Include(include);
                }

                return query.AsNoTracking()
                    .Where(x => x.Id.Equals(id)).FirstOrDefault();                    
            }
        }

But it didn't compile. I got this error:

Cannot implicitly convert type 'System.Data.Entity.Infrastructure.DbQuery' to 'System.Data.Entity.DbSet'. An explicit conversion exists (are you missing a cast?)

Does anyone know how to make the list of Includes dynamic?

Thanks

2012-04-03 20:41
by LeoD
I did a plugin that do just that here is the link https://www.codeproject.com/Tips/1205294/Entity-Framework-Dynamic-Include-Hierarch - Alen.Toma 2017-09-11 16:48


9

Make the query variable queryable:

public CampaignCreative GetCampaignCreativeById(int id, string[] includes)
{
    using (var db = GetContext())
    {
        var query = db.CampaignCreatives.AsQueryable();
        foreach (string include in includes)
        {
            query = query.Include(include);
        }

        return query
            .AsNoTracking()
            .Where(x => x.Id.Equals(id))
            .FirstOrDefault();                    
    }
}
2012-04-03 20:46
by Darin Dimitrov
What version of EntityFramework does this require - cmour 2013-01-24 20:58
I don't get it. IQueryable has no Include method. This doesn't compile - dudeNumber4 2014-03-12 18:00
Some projects you will get a compile error with this solution. Add using System.Data.Entity; to access the Include extension method on IQueryabl - ericstaples 2014-08-08 04:31
This does not work For EF4.0. Missing Include extensio - Davut Gürbüz 2015-01-12 14:32
IQuerable does not have Include extension for EF4.0. For EF4.0 you oppositely cast IQuerable to ObjectQuery. Then you can include details - Davut Gürbüz 2015-01-12 15:23


26

I am more fond of the non-string expressive way of defining includes. Mainly because it doesn't rely on magic strings.

For the example code, it would look something like this:

public CampaignCreative GetCampaignCreativeById(int id) {
    using (var db = GetContext()) {
        return db.CampaignCreatives
            .Include(cc => cc.Placement)
            .Include(cc => cc.CreativeType)                    
            .Include(cc => cc.Campaign.Select(c => 
                 c.Handshake.Select(h => h.Agency)))
            .Include(cc => cc.Campaign.Select(c => c.Product)
            .AsNoTracking()
            .Where(x => x.Id.Equals(id))
            .FirstOrDefault();
    }
}

And to make those dynamic, this is how you do that:

public CampaignCreative GetCampaignCreativeById(
    int id, 
    params Expression<Func<T, object>>[] includes
) {
    using (var db = GetContext()) {
        var query = db.CampaignCreatives;
        return includes
            .Aggregate(
                query.AsQueryable(), 
                (current, include) => current.Include(include)
            )
            .FirstOrDefault(e => e.Id == id);
    }
}

Which is used like this:

var c = dataService.GetCampaignCreativeById(
     1, 
     cc => cc.Placement, 
     cc => cc.CreativeType, 
     cc => cc.Campaign.Select(c => c.Handshake.Select(h => h.Agency)),
     cc => cc.Campaign.Select(c => c.Product
);
2013-10-04 11:09
by Mikael Östberg
That's really nice - pomeroy 2013-10-21 12:28
I said it above but I'll say it here in case anyone missed it: Some projects you will get a compile error with this solution. Add using System.Data.Entity; to access the Include extension method on IQueryable - ericstaples 2014-08-08 04:32
very nice, exactly what i was trying to acheive - Kramer00 2015-12-04 09:21
This answer should've been the one checked up. I found it extremely useful when it comes to EF strongly typed navigation properties on generic types - yopez83 2018-05-02 19:12


1

Giving the compiler a hint by using IQueryable<CampaignCreative> instead of var will work too.

IQueryable<CampaignCreative> query = db.CampaignCreatives;
// or
DbQuery<CampaignCreative> query = db.CampaignCreatives;

When using var the compiler infers DbSet<T> for query which is more specific than the type returned by Include (which is DbQuery<T> (=base class of DbSet<T>) implementing IQueryable<T>), so you can't assign the result to the query variable anymore. Hence the compiler error on the query = query.Include(include) line.

2012-04-03 22:27
by Slauma