使用.Find()& .Include()在同一个查询上

时间:2013-07-10 16:43:26

标签: entity-framework

我从带有存储库的脚手架模板自动生成以下方法: -

public Group Find(int id)
{
    return context.Groups.Find(id);
}

但是由于Groups对象有两个我需要的导航属性,所以我想要包含.Include,所以我将.find替换为.where: -

public Group Find(int id)
{
    return context.Groups.Where(c=>c.GroupID==id)
                         .Include(a => a.UserGroups)
                         .Include(a2 => a2.SecurityRoles)
                         .SingleOrDefault();
}

但我的问题是如何将.Include应用于.find()而非使用.Where()

3 个答案:

答案 0 :(得分:39)

我只是想着实际发现了什么。 @lazyberezovsky是正确的包含并且找不到彼此结合使用。我认为这是非常慎重的,这就是原因:

  

DbSet上的Find方法使用主键值尝试查找   上下文跟踪的实体。如果在实体中找不到实体   然后将上下文查询发送到数据库以查找实体   那里。如果在上下文中找不到实体,则返回Null   在数据库中。

     

查找与以两种重要方式使用查询不同:

     
      
  • 只有在上下文中找不到具有给定密钥的实体时,才会往返数据库。
  •   
  • 查找将返回处于已添加状态的实体。也就是说,Find将返回已添加到上下文但具有的实体   尚未保存到数据库中。
  •   

(来自http://msdn.microsoft.com/en-us/data/jj573936.aspx

因为find是一种优化方法,所以可以避免需要访问服务器。如果您已经跟踪了实体,这很好,因为EF可以更快地返回它。

然而,如果它不仅仅是我们所追求的这个实体(例如我们想要包含一些额外数据),那么无法知道这些数据是否已经从服务器加载。虽然EF可能会与连接一起进行此优化,但它会因为对数据库状态做出假设而容易出错。

我认为包含并发现​​无法一起使用是一个非常慎重的决定,以确保数据完整性和不必要的复杂性。它更清洁,更简单  如果您想要进行连接,请始终转到数据库以执行该连接。

答案 1 :(得分:6)

你做不到。在Find类型上定义的DbSet<T>方法,它返回实体。您无法在实体上调用Include,因此唯一可能的选择是在 Find之后调用Include 。您需要DbSet<T>类型,但Include("UserGroups")将返回DbQuery<T>Include(g => g.UserGroups)也会返回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>不是DbSet<T>的孩子,因此方法Find不可用。还要记住,Find首先在本地对象中查找实体。如果它们尚未加载,它将如何包含一些引用的实体?

答案 2 :(得分:0)

您可以尝试执行以下操作:

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;
    }

方法包括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;
    }