0

I am struggling to inject a class and its subclass properties from source to target.

For instance, I am getting objects like below from the return value of a method. I need to inject it into a target type.

Class A
{
  prop A
  prop B
}

Class B : A
{
  prop C
  prop D
}

I wrote the below code for array and value checking, but couldn't get an idea on how to write for class i.e "IsClass". No where in internet I got an idea as my googling says they have for IsGenericType.

Please help me

protected override object SetValue(ConventionInfo c)
        {
            if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string)
                || c.TargetProp.Type.IsValueType || c.TargetProp.Type == typeof(string))
                return c.SourceProp.Value;

            if (c.SourceProp.Type.IsArray)
            {
                var arr = c.SourceProp.Value as Array;
                if (arr != null)
                {
                    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 is string) continue;
                        if (clone != null)
                            clone.SetValue(Activator.CreateInstance(c.TargetProp.Type.GetElementType()).InjectFrom<CloneInjection>(a), index);
                    }
                    return clone;
                }
            }

  **If c.SourceProp.Type.IsClass Then
                Return Activator.CreateInstance(c.SourceProp.Type).InjectFrom(Of CloneInjection)(c.SourceProp.Value)
            End If**

            return Activator.CreateInstance(c.TargetProp.Type)
                .InjectFrom<CloneInjection>(c.SourceProp.Value);
        }

UPDATE

I added the last IsClass condition. Still same exception that I used to get without adding it.

An exception of type 'System.MissingMemberException' occurred in Microsoft.VisualBasic.dll but was not handled in user code.

Additional information: Public member 'InjectFrom' on type 'B' not found.

Community
  • 1
  • 1
Jasmine
  • 5,186
  • 16
  • 62
  • 114
  • basically you would move corresponding field, from an object to another. But is not clear to me which rule you must follow, you would map only omonimouse member ? Another question you would map also private members? – Skary Dec 16 '15 at 22:04
  • @Skary: Yes, ALL members. My requirement is simple. I have a type from a DLL(Model) and another type from another DLL (Service). So basically I need to convert Model to Service type. Dont know what is wrong in my code. – Jasmine Dec 16 '15 at 22:43
  • at first sight it seems that there are binding problem, because B is defined as base property of B and is not a property of B itself (i suppose). I post an answer based on similar work i made time ago, i can not test now but may be useful as starting point. – Skary Dec 16 '15 at 22:48
  • @Skary: Thank you, it works well in our C# project (Perhaps little changes in their types). But I need it on VB (With a little different type) – Jasmine Dec 16 '15 at 22:58
  • you're using an older version of the cloneinjection, maybe the new one will work better for you https://github.com/omuleanu/ValueInjecter/blob/master/Tests/Injections/CloneInjection.cs InjectFrom is an object extension, not sure how VB works, but in c# the code would not compile unless you use the dynamic keyword – Omu Dec 17 '15 at 16:11
  • @Omu: Thank you very much, you know I was really struggling with it as I have many things to convert. In VB, it was a nightmare, and also, I was looking for code to check IsClass condition and do necessary, but unfortunately no where in web and or your article gives that code although you commented that simply use instance and use cloneinjection if its a class. Didn't help me, so ended up with Automapper. ANyway appreciate your efforts. In fact, my architects suggested that yours is faster, but being a newbie, it was a painstaking thing. – Jasmine Dec 17 '15 at 23:42

1 Answers1

0

Deevinee as I said in the comment here I post a solution that I wrote for a similar issue I had in C#. I hope it could help you.

I've decided to write a static method for mapping corresponding field of two instance of the same type or with different type but with the same property name.

I have tried to adapt code on the fly now, but without the ability of test it, so forgive me if there are some compilation errors.

    public static void Map (object objFrom, object objTo, StringComparison comparisionType = StringComparison.InvariantCulture)
    {
        if (objFrom != null && objTo != null)
        {
            PropertyInfo[] propTo = objTo.GetType().GetProperties( /*Optional binding flag to get private property use field instead to get each field not only properties*/ );

            foreach (PropertyInfo prop in objFrom.GetType().GetProperties( /*Optional binding flag to get private property use field instead to get each field not only properties*/) )
            {
                if ( !prop.CanRead )
                    continue;

                try
                {
                    object valFrom = objFrom.GetType().InvokeMember(prop.Name, BindingFlags.GetProperty /*as before play with binding flag here*/, null, objFrom, null);
                    PropertyInfo target = propTo.FirstOrDefault(x => x.Name.Equals(prop.Name, comparisionType));

                    if ( target == null || !target.CanWrite )
                        continue;

                    if (target.PropertyType.IsGenericType && target.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                        {
                            if (prop.PropertyType.Equals(target.PropertyType))
                                objTo.GetType().InvokeMember(target.Name, BindingFlags.SetProperty, null, objTo, new object[1] { valFrom });
                        }
                        else
                        {
                            if (prop.PropertyType.Equals(Nullable.GetUnderlyingType(target.PropertyType)))
                                objTo.GetType().InvokeMember(target.Name, BindingFlags.SetProperty , null, objTo, new object[1] { valFrom });
                        }
                    }
                    else
                    {
                        if (prop.PropertyType.Equals(target.PropertyType))
                            objTo.GetType().InvokeMember(target.Name, BindingFlags.SetProperty, null, objTo, new object[1] { valFrom });
                    }
                }
                catch { }
            }
        }
    }

Here's some information about Binding: https://msdn.microsoft.com/it-it/library/kyaxdd3x(v=vs.110).aspx

If you would you could apply in this method a function (passed as arguments) for decide which properties need to be mapped each other instead of rely on names equality (max flexibility).

PiotrWolkowski
  • 8,408
  • 6
  • 48
  • 68
Skary
  • 1,322
  • 1
  • 13
  • 40