c# español - Entity Framework 5 Actualizando un Registro




mvc tutorial (7)

Estás buscando:

db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();

He estado explorando diferentes métodos para editar / actualizar un registro dentro de Entity Framework 5 en un entorno ASP.NET MVC3, pero hasta ahora ninguno de ellos marca todos los cuadros que necesito. Voy a explicar por que

He encontrado tres métodos a los que voy a mencionar los pros y los contras:

Método 1 - Cargar registro original, actualizar cada propiedad

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    original.BusinessEntityId = updatedUser.BusinessEntityId;
    original.Email = updatedUser.Email;
    original.EmployeeId = updatedUser.EmployeeId;
    original.Forename = updatedUser.Forename;
    original.Surname = updatedUser.Surname;
    original.Telephone = updatedUser.Telephone;
    original.Title = updatedUser.Title;
    original.Fax = updatedUser.Fax;
    original.ASPNetUserId = updatedUser.ASPNetUserId;
    db.SaveChanges();
}    

Pros

  • Puede especificar qué propiedades cambian
  • Las vistas no necesitan contener cada propiedad

Contras

  • 2 x consultas en la base de datos para cargar el original y luego actualizarlo

Método 2 - Cargar registro original, establecer valores modificados

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    db.Entry(original).CurrentValues.SetValues(updatedUser);
    db.SaveChanges();
}

Pros

  • Sólo las propiedades modificadas se envían a la base de datos

Contras

  • Las vistas deben contener cada propiedad.
  • 2 x consultas en la base de datos para cargar el original y luego actualizarlo

Método 3: adjunte el registro actualizado y establezca el estado a EntityState.Modified

db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();

Pros

  • 1 x consulta en la base de datos para actualizar

Contras

  • No se puede especificar qué propiedades cambian
  • Las vistas deben contener cada propiedad

Pregunta

Mi pregunta para ustedes, chicos; ¿Hay una manera limpia de que pueda lograr este conjunto de objetivos?

  • Puede especificar qué propiedades cambian
  • Las vistas no necesitan contener todas las propiedades (como la contraseña)
  • 1 x consulta en la base de datos para actualizar

Entiendo que esto es una cosa menor que señalar, pero me puede faltar una solución simple para esto. Si no es el método uno prevalecerá ;-)


Realmente me gusta la respuesta aceptada. Creo que hay otra manera de abordar esto también. Supongamos que tiene una lista muy corta de propiedades que no querría incluir en una Vista, por lo que al actualizar la entidad, se omitirán. Digamos que esos dos campos son Contraseña y SSN.

db.Users.Attach(updatedUser);

var entry = db.Entry(updatedUser);
entry.State = EntityState.Modified;

entry.Property(e => e.Password).IsModified = false;
entry.Property(e => e.SSN).IsModified = false;   

db.SaveChanges();   

Este ejemplo le permite esencialmente dejar su lógica de negocios solo después de agregar un nuevo campo a su tabla de Usuarios y a su Vista.


public interface IRepository
{
    void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class;
}

public class Repository : DbContext, IRepository
{
    public void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class
    {
        Set<T>().Attach(obj);
        propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true);
        SaveChanges();
    }
}

Solo para agregar a la lista de opciones. También puede tomar el objeto de la base de datos y usar una herramienta de mapeo automático como Auto Mapper para actualizar las partes del registro que desea cambiar.


foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) {
    if (propertyInfo.GetValue(updatedUser, null) == null)
        propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null);
}
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();

Dependiendo de su caso de uso, se aplican todas las soluciones anteriores. Así es como normalmente lo hago sin embargo:

Para el código del lado del servidor (por ejemplo, un proceso por lotes), por lo general carga las entidades y trabajo con proxies dinámicos. Por lo general, en los procesos por lotes debe cargar los datos de todos modos en el momento en que se ejecuta el servicio. Intento cargar por lotes los datos en lugar de usar el método de búsqueda para ahorrar algo de tiempo. Dependiendo del proceso, uso un control de concurrencia optimista o pesimista (siempre uso el optimismo, excepto en los escenarios de ejecución paralela en los que necesito bloquear algunos registros con sentencias de sql sin formato, aunque esto es raro). Dependiendo del código y el escenario, el impacto se puede reducir a casi cero.

Para los escenarios del lado del cliente, tiene algunas opciones.

  1. Utilizar modelos de vista. Los modelos deben tener una propiedad UpdateStatus (sin modificar-insertada-actualizada-eliminada). Es responsabilidad del cliente establecer el valor correcto en esta columna dependiendo de las acciones del usuario (insertar-actualizar-eliminar). El servidor puede consultar la base de datos para los valores originales o el cliente debe enviar los valores originales al servidor junto con las filas modificadas. El servidor debe adjuntar los valores originales y usar la columna UpdateStatus para cada fila para decidir cómo manejar los nuevos valores. En este escenario siempre uso la concurrencia optimista. Esto solo hará las instrucciones insertar - actualizar - eliminar y no las selecciones, pero podría necesitar algún código inteligente para recorrer el gráfico y actualizar las entidades (depende de su escenario - aplicación). Un mapeador puede ayudar pero no maneja la lógica CRUD

  2. Utilice una biblioteca como breeze.js que oculta la mayor parte de esta complejidad (como se describe en 1) e intente adaptarla a su caso de uso.

Espero eso ayude


En mi opinión, el mejor enfoque aquí es un método de inicialización, es decir,

interface ISomeInterface {
    void Init(int i);
}
class Foo : ISomeInterface {
    void ISomeInterface.Init(int i) { /* ... */ }
}
static class Program {
    static T Create<T>(int i) where T : class, ISomeInterface, new() {
        T t = new T();
        t.Init(i);
        return t;
    }
    static void Main() {
        Foo foo = Create<Foo>(123);
    }
}

Sin embargo, puede hacer lo que quiera con Expression (pero sin soporte en tiempo de compilación):

using System;
using System.Linq.Expressions;
class Foo {
    public Foo(int i) { /* ... */ }
}
static class Program {
    static T Create<T>(int i) {
        return CtorCache<T>.Create(i);
    }
    static class CtorCache<T> {
        static Func<int, T> ctor;
        public static T Create(int i) {
            if (ctor == null) ctor = CreateCtor();
            return ctor(i);
        }
        static Func<int, T> CreateCtor() {
            var param = Expression.Parameter(typeof(int), "i");
            var ci = typeof(T).GetConstructor(new[] {typeof(int)});
            if(ci == null) throw new InvalidOperationException("No such ctor");
            var body = Expression.New(ci, param);
            return Expression.Lambda<Func<int, T>>(body, param).Compile();
        }
    }
    static void Main() {
        Foo foo = Create<Foo>(123);
    }
}

Tenga en cuenta que esto almacena en caché y reutiliza al delegado para el rendimiento.





c# asp.net-mvc-3 entity-framework-5