38

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

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

但我的问题是如何应用.Includewith.find()而不是 using .Where()

4

3 回答 3

41

我只是在想 find 实际上做了什么。@lazyberezovsky 是正确的 include 和 find 不能相互结合使用。我认为这是故意的,原因如下:

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

查找与使用查询在两个重要方面不同:

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

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

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

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

我想 include 和 find 不能一起使用是一个非常慎重的决定,以确保数据完整性和不必要的复杂性。当您想要进行连接以始终访问数据库来执行连接时,它会更加干净和简单。

于 2013-07-10T22:22:42.580 回答
7

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

于 2013-07-10T17:01:50.337 回答
1

您可以尝试这样做:

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(看这里(EF6 和 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;
    }
于 2019-11-19T11:07:51.893 回答