[英]How to combine Find() and AsNoTracking()?
在對 EF 上下文進行查詢時如何將Find()
與AsNoTracking()
結合使用,以防止跟蹤返回的對象。 這是我做不到的
_context.Set<Entity>().AsNoTracking().Find(id);
我怎樣才能做到這一點? 我正在使用 EF 版本 6。
注意:我不想使用SingleOrDefault()
或Where
。 我只是不能,因為參數Id
是通用的並且它是一個struct
,在這種情況下我不能將 operator ==
應用於泛型。
因此,您可以做的不是使用AsNoTracking()
,而是Find()
,然后將其從上下文中分離出來。 我相信這會給您與AsNoTracking()
相同的結果,除了跟蹤實體的額外開銷。 有關詳細信息,請參閱EntityState 。
var entity = Context.Set<T>().Find(id);
Context.Entry(entity).State = EntityState.Detached;
return entity;
編輯:這有一些潛在的問題,如果上下文沒有加載一些關系,那么這些導航屬性將不起作用,你會感到困惑和沮喪,為什么一切都返回 null! 有關更多信息,請參閱https://stackoverflow.com/a/10343174/2558743 。 現在,在那些存儲庫中,我正在覆蓋我需要的存儲庫中的FindNoTracking()
方法。
<context>.<Entity>.AsNoTracking().Where(s => s.Id == id);
Find()
對AsNoTracking()
沒有意義,因為Find
應該能夠在不進入數據庫的情況下返回跟蹤的實體AsNoTracking
的唯一選擇是Where
或First
或Single...
接受的答案的問題是,如果您嘗試查找的項目已被跟蹤,它將返回該項目然后將其標記為未跟蹤(這可能會弄亂代碼的其他部分)。
Akos 的建議是自己構建表達式的做法是正確的,但該示例僅適用於具有單個主鍵的實體(涵蓋大多數情況)。
此擴展方法適用於 EF Core,並有效匹配DbSet<T>.Find(object [])
的簽名。 但它是DbContext
而不是DbSet
的擴展方法,因為它需要從 DbContext 訪問實體的元數據。
public static T FindNoTracking<T>(this DbContext source, params object[] keyValues)
where T : class
{
DbSet<T> set = source.Set<T>();
if (keyValues == null || !keyValues.Any())
{
throw new Exception("No Keys Provided.");
}
PropertyInfo[] keyProps = GetKeyProperties<T>(source);
if (keyProps.Count() != keyValues.Count())
{
throw new Exception("Incorrect Number of Keys Provided.");
}
ParameterExpression prm = Expression.Parameter(typeof(T));
Expression body = null;
for (int i = 0; i < keyProps.Count(); i++)
{
PropertyInfo pi = keyProps[i];
object value = keyValues[i];
Expression propertyEx = Expression.Property(prm, pi);
Expression valueEx = Expression.Constant(value);
Expression condition = Expression.Equal(propertyEx, valueEx);
body = body == null ? condition : Expression.AndAlso(body, condition);
}
var filter = Expression.Lambda<Func<T, bool>>(body, prm);
return set.AsNoTracking().SingleOrDefault(filter);
}
public static PropertyInfo[] GetKeyProperties<T>(this DbContext source)
{
return source.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(p => p.PropertyInfo).ToArray();
}
然后,您可以直接在 DbContext 上使用該方法。 例如,如果您的實體有一個由兩個字符串組成的復合鍵:
context.FindNoTracking<MyEntity>("Key Value 1", "Key Value 2");
如果您確實希望 Extension 方法位於DbSet
而不是DbContext
上,則可以這樣做,但您需要從集合中獲取上下文才能訪問有關實體的元數據。 目前沒有很好的方法來做到這一點。 有一些 hacky 方法可以做到這一點,但它們涉及使用反射來訪問框架類的私有字段,所以我建議不要這樣做。
或者...
如果您有辦法在不使用 DbContext/Metadata 的情況下確定 Key 屬性是什么,則可以改為將其作為DbSet
的擴展。 例如,如果您的所有 Key 屬性都標有[Key]
屬性,則可以使用以下代碼:
public static T FindNoTracking<T>(this DbSet<T> source, params object[] keyValues)
where T : class
{
//Pretty much the same...
}
public static PropertyInfo[] GetKeyProperties<T>()
{
return typeof(T).GetProperties()
.Where(pi => pi.GetCustomAttribute<KeyAttribute>() != null).ToArray();
}
這也適用於 Entity Framework 和 EF Core。
好吧,我想如果你真的想這樣做,你可以嘗試自己創建你的表情。 我假設您有一個通用的基本實體類,這就是通用鍵屬性的來源。 我將該類命名為KeyedEntityBase<TKey>
, TKey
是鍵的類型(如果您沒有這樣的類,那很好,我唯一使用它的是通用約束)。 然后你可以創建一個像這樣的擴展方法來自己構建表達式:
public static class Extensions
{
public static IQueryable<TEntity> WhereIdEquals<TEntity, TKey>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, TKey>> keyExpression,
TKey otherKeyValue)
where TEntity : KeyedEntityBase<TKey>
{
var memberExpression = (MemberExpression)keyExpression.Body;
var parameter = Expression.Parameter(typeof(TEntity), "x");
var property = Expression.Property(parameter, memberExpression.Member.Name);
var equal = Expression.Equal(property, Expression.Constant(otherKeyValue));
var lambda = Expression.Lambda<Func<TEntity, bool>>(equal, parameter);
return source.Where(lambda);
}
}
然后,您可以像這樣使用它(對於整數鍵類型):
context.Set<MyEntity>.AsNoTracking().WhereIdEquals(m=>m.Id, 9).ToList();
我知道這是一個老問題,但這應該取代“查找”:
var variable = <context>.<Entity>.AsNoTracking().FirstOrDefault(s => s.Id == id)
然后檢查variable!=null
早在 2015 年,官方就要求包含該功能,即結合 Find() 和 AsNoTracking()。 給出這個論點后,該問題立即關閉:
AsNoTracking
對於Find
並沒有真正意義,因為 find 的關鍵特性之一是如果它已經在內存中,它將返回已跟蹤的實體版本,而不會訪問數據庫。 如果您想按鍵加載實體而不跟蹤它,請使用Single
。
因此,您可以替換:
_context.Set<Entity>().AsNoTracking().Find(id); // Invalid
像這樣:
_context.Set<Entity>().AsNoTracking().Single(e => e.Id == id);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.