Подтверждение платформы Entity Framework с частичными обновлениями

Я использую Entity Framework 5.0 с объектами DbContext и POCO. Там простой объект, содержащий 3 свойства:

public class Record
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsActive { get; set; }
}

Поле Title всегда немодифицировано, и пользовательский интерфейс просто отображает его без предоставления какого-либо окна ввода для его изменения. Поэтому для поля Title установлено значение null, когда форма отправляется на сервер.

Здесь, как я говорю EF для выполнения частичного обновления объекта (только IsActive):

public class EFRepository<TEntity>
{
   ...
   public void PartialUpdate(TEntity entity, params Expression<Func<TEntity, object>>[] propsToUpdate)
   {
       dbSet.Attach(entity);
       var entry = _dbContext.Entry(entity);
       foreach(var prop in propsToUpdate)
           contextEntry.Property(prop).IsModified = true;
   }
}

и вызов:

repository.PartialUpdate(updatedRecord, r => r.IsActive);

Вызов метода SaveChanges, я получаю DbEntityValidationException, который говорит мне, Title требуется. Когда я устанавливаю dbContext.Configuration.ValidateOnSaveEnabled = false, все в порядке. Есть ли способ избежать отключения проверки во всем контексте и сообщить EF не проверять свойства, которые не обновляются? Спасибо заранее.

Ответ 1

Если вы используете частичные обновления или объекты-заглушки (оба подхода довольно эффективны!), вы не можете использовать глобальную проверку EF, потому что не учитывает частичные изменения - it всегда проверяет целостность объекта. С логикой проверки по умолчанию вы должны отключить ее, позвонив по ссылке:

dbContext.Configuration.ValidateOnSaveEnabled = false

И проверяйте каждое обновленное свойство отдельно. Это, надеюсь, сделает магию, но я не пробовал, потому что я вообще не использую валидацию EF:

foreach(var prop in propsToUpdate) {
    var errors = contextEntry.Property(prop).GetValidationErrors();
    if (erros.Count == 0) {
        contextEntry.Property(prop).IsModified = true;
    } else {
        ...
    }
}

Если вы хотите пойти дальше, вы можете попробовать переопределить ValidateEntity в своем контексте и переопределить проверку таким образом, чтобы он проверял всю сущность или только выбранные свойства на основе состояния объекта и состояния свойств t23. позволит вам использовать проверку EF с частичными обновлениями и заглушками.

Валидация в EF - это неправильная концепция IMHO - она ​​вводит дополнительную логику в уровень доступа к данным, где логика не принадлежит. Это в основном основано на идее, что вы всегда работаете со всей сущностью или даже с целым графом объекта, если вы разместите необходимые правила проверки свойств навигации. Как только вы нарушите этот подход, вы всегда обнаружите, что один фиксированный набор правил проверки, жестко закодированных для ваших объектов, недостаточно.

Одна из вещей, которые у меня есть в моем очень длинном отставании, заключается в том, чтобы выяснить, насколько валидация влияет на скорость операции SaveChanges - у меня был собственный API проверки в EF4 (до EF4.1) на основе DataAnnotations и их Validator, и я прекратил использовать его довольно скоро из-за очень низкой производительности.

Обходной путь с использованием собственного SQL имеет тот же эффект, что и при использовании объектов-заглушек или частичных обновлений с выключенной проверкой = ваши объекты еще не проверены, но, кроме того, ваши изменения не являются частью одной единицы работы.

Ответ 2

В отношении Ladislav answer, я добавил это в класс DbContext, и теперь он удаляет все свойства, которые не изменяются.
Я знаю, что он не полностью пропускает валидацию этих свойств, а просто опускает его, но EF проверяет на предмет не свойство, и перезапись всего процесса проверки заново была слишком сложной для меня.

protected override DbEntityValidationResult ValidateEntity(
  DbEntityEntry entityEntry,
  IDictionary<object, object> items)
{
  var result = base.ValidateEntity(entityEntry, items);
  var falseErrors = result.ValidationErrors
    .Where(error =>
    {
      if (entityEntry.State != EntityState.Modified) return false;
      var member = entityEntry.Member(error.PropertyName);
      var property = member as DbPropertyEntry;
      if (property != null)
        return !property.IsModified;
      else
        return false;//not false err;
    });

  foreach (var error in falseErrors.ToArray())
    result.ValidationErrors.Remove(error);
  return result;
}

Ответ 3

Это ремикс предыдущего ответа @Shimmy, и это версия, которую я сейчас использую.

То, что я добавил, - это предложение (entityEntry.State != EntityState.Modified) return false; в Where:

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
    var result = base.ValidateEntity(entityEntry, items);

    var falseErrors = result
        .ValidationErrors
        .Where(error =>
        {
            if (entityEntry.State != EntityState.Modified) return false;
            var member = entityEntry.Member(error.PropertyName);
            var property = member as DbPropertyEntry;
            if (property != null) return !property.IsModified;
            return false;
        });

    foreach (var error in falseErrors.ToArray())
    {
        result.ValidationErrors.Remove(error);
    }

    return result;
}