Validación de Entity Framework con actualizaciones parciales

Estoy usando Entity Framework 5.0 con las entidades DbContext y POCO. Hay una entidad simple que contiene 3 propiedades:

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

El campo Título siempre está sin modificar, y la IU simplemente lo muestra sin proporcionar ningún cuadro de entrada para modificarlo. Es por eso que el campo Title se establece en null cuando el formulario se envía al servidor.

Así es como le digo a EF que realice la actualización parcial de la entidad (campo IsActive solamente):

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

y la llamada:

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

Al SaveChanges método SaveChanges , obtengo la DbEntityValidationException , que me dice que se requiere un Title . Cuando configuro dbContext.Configuration.ValidateOnSaveEnabled = false , todo está bien. ¿Hay alguna manera de evitar la desactivación de la validación en todo el contexto y decirle a EF que no valide las propiedades que no se actualizan? Gracias por adelantado.

Si usa actualizaciones parciales o entidades de stub (¡ambos enfoques son bastante válidos!) No puede usar la validación de EF global porque no respeta sus cambios parciales , siempre valida la entidad completa. Con la lógica de validación por defecto, debe desactivarla llamando al mencionado:

 dbContext.Configuration.ValidateOnSaveEnabled = false 

Y valide cada propiedad actualizada por separado. Esto debería hacer la magia pero no lo intenté porque no uso la validación EF en absoluto:

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

Si quieres dar un paso más, puedes reemplazar ValidateEntity en tu contexto y reimplementar la validación en la forma en que valida la entidad completa o solo las propiedades seleccionadas según el estado de la entidad y el estado de propiedades IsModified , lo que te permitirá usar la validación de EF con actualizaciones parciales y entidades de stub.

La validación en EF es un concepto erróneo en mi humilde opinión: introduce una lógica adicional en la capa de acceso a datos a la que la lógica no pertenece. Se basa principalmente en la idea de que siempre trabajas con una entidad completa o incluso con un gráfico de entidades completas si colocas las reglas de validación necesarias en las propiedades de navegación. Una vez que viola este enfoque, siempre encontrará que un solo conjunto fijo de reglas de validación codificadas en sus entidades no es suficiente.

Una de las cosas que tengo en mi larga lista es investigar cómo la validación afecta la velocidad de la operación SaveChanges . Solía ​​tener mi propia API de validación en EF4 (anterior a EF4.1) basada en DataAnnotations y su clase Validator y dejé de usarla bastante pronto debido a un rendimiento muy pobre.

La solución con el uso de SQL nativo tiene el mismo efecto que el uso de entidades de código auxiliar o actualizaciones parciales con validación desactivada = sus entidades todavía no están validadas, pero además sus cambios no son parte de la misma unidad de trabajo.

En referencia a la respuesta de Ladislav , he agregado esto a la clase DbContext , y ahora elimina todas las propiedades que no se modifican.
Sé que no omite por completo la validación de esas propiedades, sino que simplemente lo omite, pero EF valida por entidad y no por propiedad, y reescribir todo el proceso de validación de nuevo fue demasiado complicado para mí.

 protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary 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; } 

Esta es una remezcla de la respuesta @Shimmy anterior y es una versión que uso actualmente.

Lo que he agregado es la cláusula (entityEntry.State != EntityState.Modified) return false; en el Where :

 protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary 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; } 
    Intereting Posts