Using .Find() & .Include() on the same query

You can try to do this:

public static class DbContextExtention
{
    public static TEntity FirstOfDefaultIdEquals<TEntity, TKey>(
        this IQueryable<TEntity> source, TKey otherKeyValue)
        where TEntity : class
    {
        var parameter = Expression.Parameter(typeof(TEntity), "x");
        var property = Expression.Property(parameter, "ID");
        var equal = Expression.Equal(property, Expression.Constant(otherKeyValue));
        var lambda = Expression.Lambda<Func<TEntity, bool>>(equal, parameter);
        return source.FirstOrDefault(lambda);
    }
    public static TEntity FirstOfDefaultIdEquals<TEntity>(
        this ObservableCollection<TEntity> source, TEntity enity)
        where TEntity : class
    {
        var value = (int)enity.GetType().GetProperty("ID").GetValue(enity, null);
        var parameter = Expression.Parameter(typeof(TEntity), "x");
        var property = Expression.Property(parameter, "ID");
        var equal = Expression.Equal(property, Expression.Constant(value));
        var lambda = Expression.Lambda<Func<TEntity, bool>>(equal, parameter);
        var queryableList = new List<TEntity>(source).AsQueryable();
        return queryableList.FirstOrDefault(lambda);
    }
}

GetById:

public virtual TEntity GetByIdInclude(TId id, params Expression<Func<TEntity, object>>[] includes)
    {
        var entry = Include(includes).FirstOfDefaultIdEquals(id);
        return entry;
    }

Method include EntityFramework Core (look here(EF6 and EF Core)):

protected IQueryable<TEntity> Include(params Expression<Func<TEntity, object>>[] includes)
    {
        IIncludableQueryable<TEntity, object> query = null;

        if (includes.Length > 0)
        {
            query = DbSet.Include(includes[0]);
        }
        for (int queryIndex = 1; queryIndex < includes.Length; ++queryIndex)
        {
            query = query.Include(includes[queryIndex]);
        }

        return query == null ? DbSet : (IQueryable<TEntity>)query;
    }

I was just thinking about what find actually does. @lazyberezovsky is right include and find cant be used in conjunction with each other. I think this is quite deliberate and here's why:

The Find method on DbSet uses the primary key value to attempt to find an entity tracked by the context. If the entity is not found in the context then a query will be sent to the database to find the entity there. Null is returned if the entity is not found in the context or in the database.

Find is different from using a query in two significant ways:

  • A round-trip to the database will only be made if the entity with the given key is not found in the context.
  • Find will return entities that are in the Added state. That is, Find will return entities that have been added to the context but have not yet been saved to the database.

(from http://msdn.microsoft.com/en-us/data/jj573936.aspx)

Because find is an optimised method it can avoid needing a trip to the server. This is great if you have the entity already tracked, as EF can return it faster.

However if its not just this entity which we are after (eg we want to include some extra data) there is no way of knowing if this data has already been loaded from the server. While EF could probably make this optimisation in conjunction with a join it would be prone to errors as it is making assumptions about the database state.

I imagine that include and find not being able to be used together is a very deliberate decision to ensure data integrity and unnecessary complexity. It is far cleaner and simpler when you are wanting to do a join to always go to the database to perform that join.


You can't. Find method defined on DbSet<T> type and it returns entity. You can't call Include on entity, so the only possible option is calling Find after Include. You need DbSet<T> type for that, but Include("UserGroups") will return DbQuery<T>, and Include(g => g.UserGroups) will also return DbQuery<T>:

public static IQueryable<T> Include<T>(this IQueryable<T> source, string path) 
    where T: class
{
    RuntimeFailureMethods.Requires(source != null, null, "source != null");
    DbQuery<T> query = source as DbQuery<T>;
    if (query != null)    
        return query.Include(path); // your case
    // ...
}

DbQuery<T> is not a child of DbSet<T> thus method Find is not available. Also keep in mind, that Find first looks for entity in local objects. How would it include some referenced entities, if they don't loaded yet?