我从带有存储库的脚手架模板自动生成以下方法: -
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()
?
答案 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;
}