Как совместить 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();