Как совместить Find() и AsNoTracking()?
Как объединить Find()
с AsNoTracking()
при создании запросов в контексте EF, чтобы предотвратить отслеживание возвращаемого объекта. Это то, что я не могу сделать
_context.Set<Entity>().AsNoTracking().Find(id);
Как я могу это сделать? Я использую EF версии 6.
Примечание. Я не хочу использовать SingleOrDefault()
или Where
. Я просто не могу, потому что параметр Id
является общим и это struct
, и я не могу применить оператор ==
для генериков в этом случае.
Ответы
Ответ 1
Поэтому вместо использования AsNoTracking()
вы можете Find()
и затем отсоединить его от контекста. Я полагаю, что это дает тот же результат, что и AsNoTracking()
кроме дополнительных затрат на отслеживание объекта. Смотрите EntityState для получения дополнительной информации.
var entity = Context.Set<T>().Find(id);
Context.Entry(entity).State = EntityState.Detached;
return entity;
Изменение: Это имеет некоторые потенциальные проблемы, если контекст не загружен некоторые отношения, то эти свойства навигации не будут работать, и вы будете смущены и расстроены, почему все возвращается ноль! См. fooobar.com/questions/670707/... для получения дополнительной информации. На данный момент в этих репозиториях я переопределяю методы FindNoTracking()
в моих репозиториях, в которых я нуждаюсь.
Ответ 2
<context>.<Entity>.AsNoTracking().Where(s => s.Id == id);
Find()
не имеет смысла с AsNoTracking()
, потому что Find
должен иметь возможность возвращать отслеживаемые объекты без перехода в базу данных. Единственный вариант с AsNoTracking
- либо Where
, либо First
или Single...
Ответ 3
Ну, я думаю, если вы действительно хотите это сделать, вы можете попробовать создать свое выражение самостоятельно. Я предполагаю, что у вас есть базовый класс сущностей, который является общим, и тот, где генерируется свойство основного ключа. Я назвал этот класс 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();
Ответ 4
Чтобы повторить поведение Find
и/или FindAsync
, вы можете использовать:
_context.EntitySet.FirstOrDefault(x => x.id == id).AsNoTracking();