Entity Cloner

I do an EntityCloner which can Clone an entity single or all the graph from one entity.

To clone an entity, I use reflection but the problem of Reflection is it’s far. The way to Clone a product is always the same. So I generate a Delegate on the fly during first call and I always use it.

For this, I use ILGenerator.Emit and ExpressionTree.

With Clone method, you can easy use ApplyPropertyChanges.

Next step is to do an ApplyPropertyChangesOnGraph.

public static class EntityObjectExtension

{

    public static T Clone<T>(this T entityObject) where T : EntityObject, new()

    {

        return EntityCloner<T>.Clone(entityObject);

    }

    public static T CloneWithGraph<T>(this T entityObject) where T : EntityObject, new()

    {

        return EntityCloner<T>.CloneWithGraph(entityObject);

    }

}

 

public static class EntityCloner<T>

    where T : EntityObject, new()

{

    private static Func<T, T> _cloneDelegate;

    private static Func<T, List<EntityObject>, T> _cloneWithGraphDelegate;

    private static Func<PropertyInfo, bool> _clonePropertyDelegateStep1;

    private static Func<PropertyInfo, bool> _clonePropertyDelegateStep2;

 

    private static Func<T, T> CloneDelegate

    {

        get

        {

            if (_cloneDelegate == null)

            {

                var generatedMethod = GenerateCloneMethod(ClonePropertyDelegateStep1, null).CreateDelegate(typeof(Func<T, List<EntityObject>, Func<EntityObject, List<EntityObject>, EntityObject>, T>)) as Func<T, List<EntityObject>, Func<EntityObject, List<EntityObject>, EntityObject>, T>;

                _cloneDelegate = entity => generatedMethod(entity, new List<EntityObject>(), Cloner.CloneEntity);

            }

            return _cloneDelegate;

        }

    }

    private static Func<T, List<EntityObject>, T> CloneWithGraphDelegate

    {

        get

        {

            if (_cloneWithGraphDelegate == null)

            {

                var generatedMethod = GenerateCloneMethod(ClonePropertyDelegateStep1, ClonePropertyDelegateStep2).CreateDelegate(typeof(Func<T, List<EntityObject>, Func<EntityObject, List<EntityObject>, EntityObject>, T>)) as Func<T, List<EntityObject>, Func<EntityObject, List<EntityObject>, EntityObject>, T>;

                _cloneWithGraphDelegate = (entity, entitiesAlreadyCloned) => generatedMethod(entity, entitiesAlreadyCloned, Cloner.CloneEntity);

            }

            return _cloneWithGraphDelegate;

        }

    }

    private static bool AllowClonePropertyStep1(PropertyInfo pi)

    {

        return !(typeof(EntityObject).IsAssignableFrom(pi.PropertyType) ||

            pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>));

    }

    public static Func<PropertyInfo, bool> ClonePropertyDelegateStep1

    {

        get

        {

            if (_clonePropertyDelegateStep1 == null)

                return pi => AllowClonePropertyStep1(pi);

            return _clonePropertyDelegateStep1;

        }

        set

        {

            if (_clonePropertyDelegateStep1 != value)

            {

                _clonePropertyDelegateStep1 = value;

                _cloneDelegate = null;

            }

        }

    }

    public static Func<PropertyInfo, bool> ClonePropertyDelegateStep2

    {

        get

        {

            if (_clonePropertyDelegateStep2 == null)

                return pi => !AllowClonePropertyStep1(pi);

            return _clonePropertyDelegateStep2;

        }

        set

        {

            if (_clonePropertyDelegateStep2 != value)

            {

                _clonePropertyDelegateStep2 = value;

                _cloneDelegate = null;

            }

        }

    }

 

    private static DynamicMethod GenerateCloneMethod(Func<PropertyInfo, bool> step1, Func<PropertyInfo, bool> step2)

    {

        var dynamicMethod = new DynamicMethod(“Clone”, typeof(T), new Type[] { typeof(T), typeof(List<EntityObject>), typeof(Func<EntityObject, List<EntityObject>, EntityObject>) });

        var cloneIlGenerator = dynamicMethod.GetILGenerator();

        cloneIlGenerator.Emit(OpCodes.Ldarg_0);

        var argNotNullLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Brtrue_S, argNotNullLabel);

        cloneIlGenerator.Emit(OpCodes.Ldnull);

        cloneIlGenerator.Emit(OpCodes.Ret);

        cloneIlGenerator.MarkLabel(argNotNullLabel);

        CopyObject(step1, step2, cloneIlGenerator, typeof(T), () => cloneIlGenerator.Emit(OpCodes.Ldarg_0), () => cloneIlGenerator.Emit(OpCodes.Ldarg_1), () => cloneIlGenerator.Emit(OpCodes.Ldarg_2));

        cloneIlGenerator.Emit(OpCodes.Ret);

        return dynamicMethod;

    }

 

    private static void CopyObject(Func<PropertyInfo, bool> step1, Func<PropertyInfo, bool> step2, ILGenerator cloneIlGenerator, Type typeT, Action getSource, Action getEntitiesList, Action getCloneLinkedEntities)

    {

        EntityAlreadyCloned(cloneIlGenerator, getEntitiesList, getSource);

        cloneIlGenerator.Emit(OpCodes.Castclass, typeT);

        var value = cloneIlGenerator.DeclareLocal(typeT);

        cloneIlGenerator.Emit(OpCodes.Stloc_S, value);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, value);

        var endExistingConditionMethodLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Brtrue, endExistingConditionMethodLabel);

        cloneIlGenerator.Emit(OpCodes.Newobj, typeT.GetConstructor(new Type[0]));

        cloneIlGenerator.Emit(OpCodes.Stloc_S, value);

        CopyProps(step1, step2, cloneIlGenerator, value, typeT, getSource, getEntitiesList, getCloneLinkedEntities);

        cloneIlGenerator.MarkLabel(endExistingConditionMethodLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc, value);

    }

 

    private static void CopyProps(Func<PropertyInfo, bool> step1, Func<PropertyInfo, bool> step2, ILGenerator cloneIlGenerator, LocalBuilder value, Type typeT, Action getSource, Action getEntitiesList, Action getCloneLinkedEntities)

    {

        foreach (var prop in typeT.GetProperties().Where(p => p.CanRead && p.CanWrite && step1 != null && step1(p)))

            ClonePerStep(step1, step2, cloneIlGenerator, value, typeT, getSource, getEntitiesList, getCloneLinkedEntities, prop);

        getEntitiesList();

        cloneIlGenerator.Emit(OpCodes.Ldloc, value);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(List<EntityObject>).GetMethod(“Add”));

        foreach (var prop in typeT.GetProperties().Where(p => p.CanRead && p.CanWrite && step2 != null && step2(p)))

            ClonePerStep(step1, step2, cloneIlGenerator, value, typeT, getSource, getEntitiesList, getCloneLinkedEntities, prop);

    }

 

    private static void ClonePerStep(Func<PropertyInfo, bool> step1, Func<PropertyInfo, bool> step2, ILGenerator cloneIlGenerator, LocalBuilder value, Type typeT, Action getSource, Action getEntitiesList, Action getCloneLinkedEntities, PropertyInfo prop)

    {

        if (typeof(ComplexObject).IsAssignableFrom(prop.PropertyType))

        {

            ConstructorInfo complexObjectCtor = prop.PropertyType.GetConstructor(new Type[0]);

            if (complexObjectCtor != null)

            {

                cloneIlGenerator.Emit(OpCodes.Ldloc, value);

                var sourceComplexTypeProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);

                var valueComplexTypeProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);

                getSource();

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“get_” + prop.Name));

                cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceComplexTypeProp);

                cloneIlGenerator.Emit(OpCodes.Newobj, complexObjectCtor);

                cloneIlGenerator.Emit(OpCodes.Stloc_S, valueComplexTypeProp);

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueComplexTypeProp);

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“set_” + prop.Name));

                CopyProps(step1, step2, cloneIlGenerator, valueComplexTypeProp, prop.PropertyType, () => cloneIlGenerator.Emit(OpCodes.Ldloc, sourceComplexTypeProp), getEntitiesList, getCloneLinkedEntities);

            }

        }

        else if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(EntityReference<>))

            CopyEntityReference(cloneIlGenerator, () => cloneIlGenerator.Emit(OpCodes.Ldloc, value), typeT, getSource, prop, false);

        else if (typeof(EntityObject).IsAssignableFrom(prop.PropertyType))

        {

            if (prop.PropertyType.GetConstructor(new Type[0]) != null)

            {

                var entityObjectProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);

                getSource();

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“get_” + prop.Name));

                cloneIlGenerator.Emit(OpCodes.Stloc_S, entityObjectProp);

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityObjectProp);

                var entityObjectNullLabel = cloneIlGenerator.DefineLabel();

                cloneIlGenerator.Emit(OpCodes.Brfalse_S, entityObjectNullLabel);

                cloneIlGenerator.Emit(OpCodes.Ldloc, value);

                getCloneLinkedEntities();

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityObjectProp);

                getEntitiesList();

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(Func<EntityObject, List<EntityObject>, EntityObject>).GetMethod(“Invoke”));

                cloneIlGenerator.Emit(OpCodes.Castclass, prop.PropertyType);

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“set_” + prop.Name));

                cloneIlGenerator.MarkLabel(entityObjectNullLabel);

            }

        }

        else if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>))

        {

            var entityCollectionSourceProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);

            getSource();

            cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“get_” + prop.Name));

            cloneIlGenerator.Emit(OpCodes.Stloc_S, entityCollectionSourceProp);

            cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityCollectionSourceProp);

            var entityCollectionNullLabel = cloneIlGenerator.DefineLabel();

            cloneIlGenerator.Emit(OpCodes.Brfalse_S, entityCollectionNullLabel);

            cloneIlGenerator.Emit(OpCodes.Ldloc, value);

            var entityCollectionValueProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);

            cloneIlGenerator.Emit(OpCodes.Newobj, prop.PropertyType.GetConstructor(new Type[0]));

            Type subEntityType = prop.PropertyType.GetGenericArguments()[0];

            if (subEntityType.GetConstructor(new Type[0]) != null)

            {

                cloneIlGenerator.Emit(OpCodes.Stloc_S, entityCollectionValueProp);

                var entityCollectionSourceEnumerator = cloneIlGenerator.DeclareLocal(typeof(IEnumerator<>).MakeGenericType(subEntityType));

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityCollectionSourceProp);

                cloneIlGenerator.Emit(OpCodes.Callvirt, prop.PropertyType.GetMethod(“GetEnumerator”));

                cloneIlGenerator.Emit(OpCodes.Stloc_S, entityCollectionSourceEnumerator);

                var startAddEntityCollectionLabel = cloneIlGenerator.DefineLabel();

                var endAddEntityCollectionLabel = cloneIlGenerator.DefineLabel();

                cloneIlGenerator.MarkLabel(startAddEntityCollectionLabel);

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityCollectionSourceEnumerator);

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator).GetMethod(“MoveNext”));

                cloneIlGenerator.Emit(OpCodes.Brfalse_S, endAddEntityCollectionLabel);

                var subEntityObject = cloneIlGenerator.DeclareLocal(subEntityType);

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityCollectionSourceEnumerator);

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator<>).MakeGenericType(subEntityType).GetMethod(“get_Current”));

                cloneIlGenerator.Emit(OpCodes.Stloc_S, subEntityObject);

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityCollectionValueProp);

                getCloneLinkedEntities();

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, subEntityObject);

                getEntitiesList();

                cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(Func<EntityObject, List<EntityObject>, EntityObject>).GetMethod(“Invoke”));

                cloneIlGenerator.Emit(OpCodes.Castclass, subEntityType);

                cloneIlGenerator.Emit(OpCodes.Callvirt, prop.PropertyType.GetMethod(“Add”, new Type[] { subEntityType }));

                cloneIlGenerator.Emit(OpCodes.Br_S, startAddEntityCollectionLabel);

                cloneIlGenerator.MarkLabel(endAddEntityCollectionLabel);

                cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityCollectionValueProp);

            }

            cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“set_” + prop.Name));

            cloneIlGenerator.MarkLabel(entityCollectionNullLabel);

        }

        else

        {

            cloneIlGenerator.Emit(OpCodes.Ldloc, value);

            getSource();

            cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“get_” + prop.Name));

            cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“set_” + prop.Name));

        }

    }

 

    internal static void CopyEntityReference(ILGenerator cloneIlGenerator, Action loadNewObject, Type typeT, Action getSource, PropertyInfo prop, bool copyNull)

    {

        var sourceEntityReferenceEntityKeyProp = cloneIlGenerator.DeclareLocal(typeof(EntityKey));

        var sourceEntityReferenceEntityKeyMemberProp = cloneIlGenerator.DeclareLocal(typeof(EntityKeyMember));

        var sourceEntityReferenceEntityKeysMemberProp = cloneIlGenerator.DeclareLocal(typeof(EntityKeyMember[]));

        var sourceEntityReferenceEntityKeysMemberLength = cloneIlGenerator.DeclareLocal(typeof(int));

        var valueEntityReferenceProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);

        var valueEntityReferenceEntityKeyProp = cloneIlGenerator.DeclareLocal(typeof(EntityKey));

        var valueEntityReferenceEntityKeysMemberProp = cloneIlGenerator.DeclareLocal(typeof(EntityKeyMember[]));

        var loopIndex = cloneIlGenerator.DeclareLocal(typeof(int));

        loadNewObject();

        cloneIlGenerator.Emit(OpCodes.Newobj, prop.PropertyType.GetConstructor(new Type[0]));

        cloneIlGenerator.Emit(OpCodes.Stloc_S, valueEntityReferenceProp);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceProp);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“set_” + prop.Name));

        getSource();

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod(“get_” + prop.Name));

        cloneIlGenerator.Emit(OpCodes.Callvirt, prop.PropertyType.GetMethod(“get_EntityKey”));

        cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeyProp);

        var entityKeyNullLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyProp);

        cloneIlGenerator.Emit(OpCodes.Brfalse_S, entityKeyNullLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceProp);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyProp);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKey).GetMethod(“get_EntityKeyValues”)); // We suppose EntityKeyValues is not null

        cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeysMemberProp);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberProp);

        cloneIlGenerator.Emit(OpCodes.Ldlen);

        cloneIlGenerator.Emit(OpCodes.Conv_I4);

        cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeysMemberLength);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberLength);

        cloneIlGenerator.Emit(OpCodes.Newarr, typeof(EntityKeyMember));

        cloneIlGenerator.Emit(OpCodes.Stloc_S, valueEntityReferenceEntityKeysMemberProp);

        var noEntityKeyValues = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberLength);

        cloneIlGenerator.Emit(OpCodes.Brfalse_S, noEntityKeyValues);

        cloneIlGenerator.Emit(OpCodes.Ldc_I4_0);

        cloneIlGenerator.Emit(OpCodes.Stloc_S, loopIndex);

        var startLoopLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.MarkLabel(startLoopLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberLength);

        cloneIlGenerator.Emit(OpCodes.Ceq);

        var endOfLoopLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Brtrue_S, endOfLoopLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberProp);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Ldelem_Ref);

        cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeyMemberProp);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceEntityKeysMemberProp);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyMemberProp);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKeyMember).GetMethod(“get_Key”));

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyMemberProp);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKeyMember).GetMethod(“get_Value”));

        cloneIlGenerator.Emit(OpCodes.Newobj, typeof(EntityKeyMember).GetConstructor(new Type[] { typeof(string), typeof(object) }));

        cloneIlGenerator.Emit(OpCodes.Stelem_Ref);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Ldc_I4_1);

        cloneIlGenerator.Emit(OpCodes.Add);

        cloneIlGenerator.Emit(OpCodes.Stloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Br_S, startLoopLabel);

        cloneIlGenerator.MarkLabel(endOfLoopLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyProp);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKey).GetMethod(“get_EntityContainerName”));

        cloneIlGenerator.Emit(OpCodes.Ldstr, “.”);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyProp);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKey).GetMethod(“get_EntitySetName”));

        cloneIlGenerator.Emit(OpCodes.Call, typeof(string).GetMethod(“Concat”, new Type[] { typeof(string), typeof(string), typeof(string) })); // We suppose EntityContainerName and EntitySetName aren’t null

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceEntityKeysMemberProp);

        cloneIlGenerator.Emit(OpCodes.Newobj, typeof(EntityKey).GetConstructor(new Type[] { typeof(string), typeof(IEnumerable<EntityKeyMember>) }));

        var noEntityKeyValuesEnd = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Br, noEntityKeyValuesEnd);

        cloneIlGenerator.MarkLabel(noEntityKeyValues);

        cloneIlGenerator.Emit(OpCodes.Newobj, typeof(EntityKey).GetConstructor(new Type[0]));

        cloneIlGenerator.MarkLabel(noEntityKeyValuesEnd);

        cloneIlGenerator.Emit(OpCodes.Callvirt, prop.PropertyType.GetMethod(“set_EntityKey”));

        if (copyNull)

        {

            var endCopyReferenceLabel = cloneIlGenerator.DefineLabel();

            cloneIlGenerator.Emit(OpCodes.Br_S, endCopyReferenceLabel);

            cloneIlGenerator.MarkLabel(entityKeyNullLabel);

            cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceProp);

            cloneIlGenerator.Emit(OpCodes.Ldnull);

            cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(T).GetMethod(“set_” + prop.Name));

            cloneIlGenerator.MarkLabel(endCopyReferenceLabel);

        }

        else

            cloneIlGenerator.MarkLabel(entityKeyNullLabel);

    }

 

    private static void EntityAlreadyCloned(ILGenerator cloneIlGenerator, Action getEntitiesList, Action getSource)

    {

        var loopIndex = cloneIlGenerator.DeclareLocal(typeof(int));

        var entitiesListCount = cloneIlGenerator.DeclareLocal(typeof(int));

        cloneIlGenerator.Emit(OpCodes.Ldc_I4_0);

        cloneIlGenerator.Emit(OpCodes.Stloc_S, loopIndex);

        getEntitiesList();

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(List<EntityObject>).GetMethod(“get_Count”));

        cloneIlGenerator.Emit(OpCodes.Stloc_S, entitiesListCount);

        var startLoopLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.MarkLabel(startLoopLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, entitiesListCount);

        cloneIlGenerator.Emit(OpCodes.Ceq);

        var endLoopLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Brtrue_S, endLoopLabel);

        var endLabel = cloneIlGenerator.DefineLabel();

        getEntitiesList();

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(List<EntityObject>).GetMethod(“get_Item”));

        var entityAlreadyCloned = cloneIlGenerator.DeclareLocal(typeof(EntityObject));

        cloneIlGenerator.Emit(OpCodes.Stloc_S, entityAlreadyCloned);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityAlreadyCloned);

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityObject).GetMethod(“get_EntityKey”));

        getSource();

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityObject).GetMethod(“get_EntityKey”));

        cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKey).GetMethod(“Equals”, new Type[] { typeof(EntityKey) }));

        var entityKeysDifferentsLabel = cloneIlGenerator.DefineLabel();

        cloneIlGenerator.Emit(OpCodes.Brfalse_S, entityKeysDifferentsLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, entityAlreadyCloned);

        cloneIlGenerator.Emit(OpCodes.Br_S, endLabel);

        cloneIlGenerator.MarkLabel(entityKeysDifferentsLabel);

        cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Ldc_I4_1);

        cloneIlGenerator.Emit(OpCodes.Add);

        cloneIlGenerator.Emit(OpCodes.Stloc_S, loopIndex);

        cloneIlGenerator.Emit(OpCodes.Br_S, startLoopLabel);

        cloneIlGenerator.MarkLabel(endLoopLabel);

        cloneIlGenerator.Emit(OpCodes.Ldnull);

        cloneIlGenerator.MarkLabel(endLabel);

    }

 

    public static T Clone(T obj)

    {

        return CloneDelegate(obj);

    }

    public static T CloneWithGraph(T obj)

    {

        return CloneWithGraph(obj, new List<EntityObject>());

    }

    internal static T CloneWithGraph(T obj, List<EntityObject> entitiesAlreadyCloned)

    {

        return CloneWithGraphDelegate(obj, entitiesAlreadyCloned);

    }

}

 

internal class Cloner

{

    private static Dictionary<Type, Delegate> _cached = new Dictionary<Type, Delegate>();

 

    internal static EntityObject CloneEntity(EntityObject entity, List<EntityObject> entitiesAlreadyCloned)

    {

        Type entityType = entity.GetType();

        if (!_cached.ContainsKey(entityType))

        {

            ParameterExpression paramE = Expression.Parameter(typeof(EntityObject), “e”);

            ParameterExpression paramL = Expression.Parameter(typeof(List<EntityObject>), “l”);

            Expression<Func<EntityObject, List<EntityObject>, EntityObject>> generatedExpression =

                Expression.Lambda<Func<EntityObject, List<EntityObject>, EntityObject>>(

                    Expression.Call(typeof(EntityCloner<>).MakeGenericType(entityType).GetMethod(“CloneWithGraph”, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { entityType, typeof(List<EntityObject>) }, null),

                        Expression.Convert(

                            paramE,

                            entityType),

                        paramL),

                    paramE,

                    paramL);

 

            _cached.Add(entityType, generatedExpression.Compile());

        }

        return _cached[entityType].DynamicInvoke(entity, entitiesAlreadyCloned) as EntityObject;

    }

}

This entry was posted in 7671, 7672, 7673, 7674. Bookmark the permalink.

15 Responses to Entity Cloner

  1. Martin says:

    Hi Matthieu
    I am playing with the Cloner code at the moment and finding it very interesting.

    I am geting an error on it at the moment but can’t track it down. Its in the IL section somewhere.

    Do you have an example of how to use the Cloner in an application as I may be doing it incorrectly?

    Its fantastic work and I would love to see it running, if you can help that would be fantastic.

    many thanks
    Martin
    mtrevena@reeds.net.au

  2. Thanks for feed back. 

    I use Nortwind DB. In My EDM, I include only Category et Product tables. I do simply this for my test:

    Products p;

    Categories c;

    using (var context = new NorthwindEntities1())

    {

        c = context.Categories.Include(“Products”).First();

        p = c.Products.First();

        var cLast = context.Categories.Where(cl => cl.CategoryID == 2).First();

    }

     

    var p2 = p.Clone();

    var p3 = p.CloneWithGraph();

    var c2 = c.Clone();

    var c3 = c.CloneWithGraph();

     

    The problem of ILGenerator.Emit is it’s very hard to debug.

    Matthieu

  3. Without your code, it’s hard to know why you have a bug but I update my method. I replace some cloneILGenerator.Emit(OpCodes.Ldloc_S, value) by cloneILGenerator.Emit(OpCodes.Ldloc, value).
    Say me please if it works fine now and if no and if it’s possible, send me your code please.
    Thanks
    Matthieu

  4. sniddy says:

    what kind of asm is that? …

  5. Matthieu MEZIL says:

    I am sorry, I don’t understand the question. What does asm mean? assembly?

  6. Dat Dinh says:

    It does not work with mine, the exception is:
    “The method or operation is not implemented.”

    please help me to work around with this, thanks

  7. Matthieu MEZIL says:

    Can you send me your project at matthieu.mezil@winwise.fr please.

    Thanks

  8. Dale says:

    Hi Matthieu,
    I’m using your code, the method Clone is okay, but the method CloneWithGraph throw an exception: “The EntityKey property can only be set when the current value of the property is null.”, what does it mean ? and how to work around with this ?
    please help
    thanks

  9. Matthieu MEZIL says:

    Strange, can you send me a code project with this exception please.

  10. Ravi says:

    Hi,

    I am able to compile your source code in my project. The requirement I have is to, make a copy of the Entity and insert it into the table. The table automatically generates the PrimaryKey.

    Assuming I have 2 tables, Customer and Address. Address has a foreign key reference to Customer.

    When I make a duplicate copy of Address using CloneWithGraph and insert it into it (context.AddToAddress(newAdd);), I get the following exception thrown.

    An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

    Do you know why I get this error?. Please send me reply to ravign@gmail.com

    Thanks
    Ravi.

  11. Yann says:

    Bonjour,

    J’aurais une question pour utiliser cette Class :
    Je la met sur le serveur RIA Services et les méthodes Clones seront automatiquement répliquées du côté client Silverlight?

    Merci d’avance

  12. meir says:

    Hello

    I used your code and i have an error is there a more up to date code than that here on the page?

    i use EF 3.5

    Thanks
    my email: meirr@advantech.co.il

  13. The mortgage loans suppose to be very useful for people, which are willing to organize their own career. As a fact, that’s not really hard to receive a short term loan.

  14. shlomiw says:

    hi matthieu,
    is your cloner dance well with EF 4?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>