Create shallow copy / clone of EntityObject subclass

We have an audit table in our database and when updated, the old and new values ​​are serialized to XML and stored in one row. Objects are currently deeply cloned, thus:

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;
}

      

While it works, it generates a huge amount of data due to related records extracted from a deep clone, with hundreds of thousands of reads from the DB per dcSer.WriteObject(memoryStream, obj)

, and the possible size of a MemoryStream is around 200MB, not to mention the amount of data returned to the DB. Not ideal.

So, I would like to make a member clone instead, since I understand that the clone member left object references and did not copy all of the Entity Framework models associated with it.

So, I did this:

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();
    }
}

      

but I am getting an invalid throwing exception because the actual type of the incoming EntityObject

is a subclass that belongs to the table itself.

I have also tried using an extension method to access MemberwiseClone()

, but the extension methods cannot access protected methods.

So how can I create a shallow copy of a generic EntityObject?

+3


source to share


3 answers


My first recommendation is to try this, which is similar to what you are doing now but worked for me with very little overhead:

DataContractSerializationUtils.SerializeToXmlString(Entity, throwExceptions);

      

Also, I have used this method with success and do not find the output to be too verbose. This is similar to what you are doing now.



    /// <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);
        }
    }

      

If none of these options seem appealing, I recommend writing a short function that uses reflection to copy any properties from the original object.

+3


source


From:

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

It's much more efficient and faster than serialization - exactly what you're looking for! It basically uses reflection to copy the required properties into a new EntityObject of the same type, and can do this in any EntityObject-derived class using generics.



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;
}

      

For example, you have a company: Customer, which has a navigation property: Orders. Then you can copy the Customer and their Orders using the above method as follows:

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

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

      

+2


source


How about CurrentValues.ToObject () in EF 6?

var shallowCopy = (TEntity)_dbContext.Entry(entity).CurrentValues.ToObject();

      

0


source







All Articles