efcore: Find and FindAsync does not support IIncludableQueryable

Is this by design? will be implemented later? something else? I couldn’t find an issue about it so I making this one to track this.

Steps to reproduce

context.Entities.Include(e=> e.OtherEntity).FindAsync(id)

The issue

I was expecting to work on IncludableQueryable. Without the Include, Find works as expected.

'IIncludableQueryable<Entity, ICollection<OtherEntity>>' does not contain a definition
for 'FindAsync' and no extension method 'FindAsync' accepting a first argument of type
'IIncludableQueryable<Entity, ICollection<OtherEntity>>' could be found
(are you missing a using directive or an assembly reference?)	

Further technical details

EF Core version: 1.1.0-preview1-final Operating system: Windows Visual Studio version: 2015

Other details about my project setup:

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 3
  • Comments: 18 (8 by maintainers)

Most upvoted comments

EF Triage: Find is not a query operator, but a convenient way to retrieve a single entity by key and save a database roundtrip if the entity you are looking for is already loaded in the context. Include only works in queries. For this case you can write:

var entity = await context.Entities
    .Include(e=> e.OtherEntity)
    .FirstOrDefaultAsync(e => e.Id == id);

@wc-matteo

Assuming you know the type and that the key isn’t composite:

var keyProperty = context.Model.FindEntityType(typeof(Blog)).FindPrimaryKey().Properties[0];

var entity = context.Blogs
    .Include(e => e.Posts)
    .FirstOrDefault(e => EF.Property<int>(e, keyProperty.Name) == id);

Similar code can be written if the problem is less constrained.

@wc-matteo

foreach (var navigation in context.Entry(entity).Navigations)
{
    navigation.Load();
}

@divega Curios to know what is the reason behind not supporting Include with Find by-design. I think it would be great if EF intelligently could:

Say you want to query X and include Y with it:

  1. If X and Y are already tracked by the context, retrieve them both from the Identity Map (that’s how it’s called in NH, is that the term in EF too?) .
  2. If X isn’t tracked by the context, query for X and Y from the DB.
  3. If X is tracked by the context buy Y isn’t, retrieve X from the Identity Map and query for Y from the DB.

I can’t think of a flaw in that, can you?

Thanks for your time Diego!

p.s. Not (at all) saying it’s urgent or even important to support it specially in these early stages of EF Core, I’m just curious why you decided it shouldn’t be supported by-design.

@gdoron yep, identity map is ubiquitous language for that concept within the EF team too 😄

@wc-matteo It will bring in all entities related to the given entity. If you want all entities related to all entities of a given type you are probably better off using Include. But you could do something like this:

var setMethod = typeof(DbContext).GetMethod("Set");

var entityType = context.Model.FindEntityType(typeof(Blog));
foreach (var navigation in entityType.GetNavigations())
{
    ((IQueryable)setMethod.MakeGenericMethod(navigation.GetTargetType().ClrType)
        .Invoke(context, null))
        .OfType<object>()
        .Load();
}

@FloMedja Include would be the way to do that, unless there is some really good reason why Include doesn’t work.

@DanielSSilva #7391 is tracking adding APIs to make it easier to use arbitrary key types/values.

@divega in my scenario case, I cannot use the predicate as e => e.Id == id. I have a generic class , where T : class and I have a TId, which can be long, string, or even composite, that is received as parameter. I’ve noticed that Find gets a param object[] keys and as it says “Finds an entity with the given primary key values.” so it works with composite keys too, meaning that finding an element with the given Id is as straightforward as

public virtual T FindById(TId id)
{
     return Context.Set<T>().Find(id);
}

That being said, since my generic parameter T is a class, cannot use the predicate of FirstOrDefault, meaning I cannot achieve something like this

public virtual T FindById(TId id, params Expression<Func<T, object>>[] includeProperties)
{
    return Context.Set<T>().Include(includeProperties).FirstOrDefault(x => x.Id = id);
}

My question is: why/how does it work with Find but does not with Include? @ajcvickers’s solution could work , but he states “and that the key isn’t composite”. Is the only way to change from where T : Class to my specific implementation?