Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

omu.valueinjecter deep clone unlike types

I think I'm missing a simple concept with valueinjecter and/or AutoMapper, but how do you deep clone a parent dto.Entity to biz.Entity and include all children?

For example, biz.person.InjectFrom(dto.person). I want the dto.person.AddressList collection to copy down to biz.person.AddressList collection, even though dto.Address and biz.Address are unlike types, but have the same property names.

My thinking is that if the Parent property names are spelled the same, e.g. AddressList, then it wouldn't matter if the 2 underlying objects were of different types. It would still copy same-named simple types like int, string, etc.

thank you

like image 296
user52212 Avatar asked Nov 23 '11 22:11

user52212


1 Answers

I was having the same issue when the arrays/lists in the objects have the same names but different types (ie a property named Animals of type ORMAnimals[] mapping to a property named Animals of type Animals[]).

With some minor tweaks to the sample code Chuck Norris has on the Deep Cloning page I got it working in my test code:

public class CloneInjection : ConventionInjection
{
    protected override bool Match(ConventionInfo c)
    {
        return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null;
    }

    protected override object SetValue(ConventionInfo c)
    {
        //for value types and string just return the value as is
        if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string)
            || c.TargetProp.Type.IsValueType || c.TargetProp.Type == typeof(string))
            return c.SourceProp.Value;

        //handle arrays
        if (c.SourceProp.Type.IsArray)
        {
            var arr = c.SourceProp.Value as Array;
            var clone = Activator.CreateInstance(c.TargetProp.Type, arr.Length) as Array;

            for (int index = 0; index < arr.Length; index++)
            {
                var a = arr.GetValue(index);
                if (a.GetType().IsValueType || a.GetType() == typeof(string)) continue;
                clone.SetValue(Activator.CreateInstance(c.TargetProp.Type.GetElementType()).InjectFrom<CloneInjection>(a), index);
            }
            return clone;
        }


        if (c.SourceProp.Type.IsGenericType)
        {
            //handle IEnumerable<> also ICollection<> IList<> List<>
            if (c.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
            {
                var t = c.TargetProp.Type.GetGenericArguments()[0];
                if (t.IsValueType || t == typeof(string)) return c.SourceProp.Value;

                var tlist = typeof(List<>).MakeGenericType(t);
                var list = Activator.CreateInstance(tlist);

                var addMethod = tlist.GetMethod("Add");
                foreach (var o in c.SourceProp.Value as IEnumerable)
                {
                    var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(o);
                    addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e);
                }
                return list;
            }

            //unhandled generic type, you could also return null or throw
            return c.SourceProp.Value;
        }

        //for simple object types create a new instace and apply the clone injection on it
        return Activator.CreateInstance(c.TargetProp.Type)
            .InjectFrom<CloneInjection>(c.SourceProp.Value);
    }
}
like image 159
Gloopy Avatar answered Oct 29 '22 04:10

Gloopy