Pregunta

Tenemos una tabla de auditoría en nuestra base de datos y, al actualizar, los valores nuevos y antiguos se serializan en XML y se almacenan en la misma fila.Actualmente, los objetos están clonados profundamente de la siguiente manera:

public EntityObject CloneEntity(EntityObject obj)
{
    DataContractSerializer dcSer = new DataContractSerializer(obj.GetType());

    MemoryStream memoryStream = new MemoryStream();

    dcSer.WriteObject(memoryStream, obj);

    memoryStream.Position = 0;

    EntityObject newObject = (EntityObject)dcSer.ReadObject(memoryStream);

    return newObject;
}

Si bien esto funciona, genera grandes cantidades de datos debido a los registros relacionados extraídos del clon profundo, con cientos de miles de lecturas de la base de datos en dcSer.WriteObject(memoryStream, obj), y un tamaño eventual de MemoryStream de unos 200 MB, sin mencionar la cantidad de datos que se vuelven a escribir en la base de datos.No es ideal.

Por lo tanto, me gustaría hacer una clonación por miembros, ya que tengo entendido que una clonación por miembros omitiría las referencias de objetos y evitaría copiar todos los modelos de Entity Framework relacionados.

Entonces hice esto:

public EntityObject CloneEntity(EntityObject obj)
{
    EntityObjectAuditable auditable = (EntityObjectAuditable)obj; // invalid cast exception

    return auditable.ShallowCopy();
}

// ....

public class EntityObjectAuditable : EntityObject
{
    public EntityObjectAuditable ShallowCopy()
    {
        return (EntityObjectAuditable)this.MemberwiseClone();
    }
}

pero obtengo una excepción de conversión no válida porque el tipo real del mensaje entrante EntityObject es una subclase relacionada con la propia tabla.

También intenté usar un método de extensión para acceder MemberwiseClone(), pero los métodos de extensión no pueden acceder a métodos protegidos.

Entonces, ¿cómo puedo crear una copia superficial de un EntityObject genérico?

¿Fue útil?

Solución

Mi primera recomendación sería probar esto, que es similar a lo que estás haciendo ahora, pero que me ha funcionado con muy pocos gastos generales:

DataContractSerializationUtils.SerializeToXmlString(Entity, throwExceptions);

Además, he usado este método antes con éxito y no encuentro que el resultado sea demasiado detallado.Parece ser casi idéntico a lo que estás haciendo ahora.

    /// <summary>
    /// Creates an exact duplicate of the entity provided
    /// </summary>
    /// <param name="source">The source copy of the entity</param>
    /// <returns>An exact duplicate entity</returns>
    public TEntity Clone(TEntity Source)
    {
        // Don’t serialize a null object, simply return the default for that object
        if (ReferenceEquals(Source, null))
        {
            return default(TEntity);
        }
        var dcs = new DataContractSerializer(typeof (TEntity));
        using (Stream stream = new MemoryStream())
        {
            dcs.WriteObject(stream, Source);
            stream.Seek(0, SeekOrigin.Begin);
            return (TEntity) dcs.ReadObject(stream);
        }
    }

Si ninguna de estas opciones parece atractiva, mi recomendación es escribir una función corta que utilice la reflexión para copiar cualquier propiedad de la entidad de origen.

Otros consejos

De:

http://www.codeproject.com/Tips/474296/Clone-an-Entity-in-Entity-Framework-4.

Es mucho más eficaz y más rápido que la serialización: ¡justo lo que estás buscando!Básicamente utiliza la reflexión para copiar las propiedades necesarias a un nuevo EntityObject del mismo tipo y puede hacerlo en cualquier clase derivada de un EntityObject haciendo uso de genéricos.

public static T CopyEntity<T>(MyContext ctx, T entity, bool copyKeys = false) where T : EntityObject
{
T clone = ctx.CreateObject<T>();
PropertyInfo[] pis = entity.GetType().GetProperties();

foreach (PropertyInfo pi in pis)
{
    EdmScalarPropertyAttribute[] attrs = (EdmScalarPropertyAttribute[])pi.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false);

    foreach (EdmScalarPropertyAttribute attr in attrs)
    {
        if (!copyKeys && attr.EntityKeyProperty)
            continue;

        pi.SetValue(clone, pi.GetValue(entity, null), null);
    }
}

return clone;
}

Por ejemplo, digamos que tenía una entidad:Cliente, que tenía la Propiedad de Navegación:Pedidos.Luego puede copiar al Cliente y sus Pedidos utilizando el método anterior de la siguiente manera:

Customer newCustomer = CopyEntity(myObjectContext, myCustomer, false);

foreach(Order order in myCustomer.Orders)
{
    Order newOrder = CopyEntity(myObjectContext, order, true);
    newCustomer.Orders.Add(newOrder);
}

¿Qué pasa con CurrentValues.ToObject() en EF 6?

var shallowCopy = (TEntity)_dbContext.Entry(entity).CurrentValues.ToObject();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top