Value Injecter with Index properties

Apr 12, 2012 at 11:33 PM

I recently ran into an issue in my Silverlight mapping where one of the classes i was trying to map implemented IDataErrorInfo. Whenever i tried to map the property - public string this[string columnName] -  a TargetParameterCountException was thrown. Ive overcome this issue by modifying the PropertyInfoStorage class with the following code:

 var props = type.GetProperties().Where(x => x.GetIndexParameters().Length == 0);                       

Storage.Add(type, props.ToArray());

 

Would this be something that we could benefit other users and possible something that I could upload to include in the Value Injecter source?

Apr 13, 2012 at 7:38 AM

well, you could as well just specify this in the Match method of a convention injection/ smartconvention

Apr 15, 2012 at 11:49 PM

The class i am mapping inherits from the class that implements that IDataErrorInfo, so I guess i would have to create another convention to map this class. And then also create conventions for any other classes that have Index properties. Since value injecter doesn't seem to be able to map these index properties, wouldn't it make sense to prevent the mapping to occur at all? or am I missing something?

Apr 16, 2012 at 9:40 AM

you can map anything, you just have to specify the way you want to do it, it would be easier if you would just post your source,target and explain from which props to which ones in target you want to inject

Apr 18, 2012 at 11:50 PM

Heres a cut down version of what I am trying to:

public class Validator

{

public string this[string columnName] {get;}

}

public Class MapFrom : Validator

{

public string Property1 {get; set;}

}

 

public Class MapTo

{

public string Property1 {get; set;}

}

I have created a ConventionInjection as the actual code im trying to map is a lot more complicated. Problem i face is that the MapFrom class derives from Validator. My convention runs fine and maps MapFrom class to MapTo class. Once its mapped all my properties in my MapFrom class it leaves my convention and goes and tries to map the properties in the validator base class. I do not want to map these properties. Furthermore, trying to map these properties throws an exception and my classes are not properly mapped once this happens. I tried creating a convention for the Validator class and set it up so it doesnt map these properties but the convention doesnt seem to get called. Not sure if this is because im not mapping this class directly.

Im wondering if the exception is thrown because its an index property, or because the target property count doesnt match the source property count?

What would be the best way to handle this scenario?

Thanks for your time!

Apr 21, 2012 at 12:24 PM

tried to replicate your problem, it works ok:

    class Program
    {
        static void Main(string[] args)
        {
            var from = new MapFrom();
            from.Property1 = "hi";
            var to = new MapTo();

            to.InjectFrom<Inj1>(from);
            Console.WriteLine(to.Property1);
        }
    }

    public class Validator
    {
        public string this[string columnName] { get { throw new NotImplementedException(); } }
    }

    public class MapFrom : Validator
    {
        public string Property1 { get; set; }
    }

    public class MapTo
    {
        public string Property1 { get; set; }
    }

    public class Inj1 : ConventionInjection
    {
        protected override bool Match(ConventionInfo c)
        {
            return c.SourceProp.Name == c.TargetProp.Name;
        }
    }

Apr 23, 2012 at 1:43 AM

Does this use the Silverlight version of ValueInjecter?

Apr 23, 2012 at 9:12 AM

no, just did new console app; install-package valueinjecter (the nuget doesn't has silverlight dlls atm) 

Apr 23, 2012 at 11:08 PM

ok. We only experience the issue with the Silverlight dll, works fine with the normal version of ValueInjecter

May 1, 2012 at 6:34 AM

So is there a nicer way to get around the problem that I am experiencing?

May 9, 2012 at 10:32 PM

ok, this works with the Silverlight version of ValueInjecter

I modified the SmartConventionInjection a bit to suit silverlight

here's the code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Omu.ValueInjecter.Silverlight;

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            var from = new MapFrom();
            from.Property1 = "hi";
            var to = new MapTo();

            to.InjectFrom<Inj1>(from);
            Console.WriteLine(to.Property1);
        }
    }

    public class Validator
    {
        public string this[string columnName] { get { throw new NotImplementedException(); } }
    }

    public class MapFrom : Validator
    {
        public string Property1 { get; set; }
    }

    public class MapTo
    {
        public string Property1 { get; set; }
    }

    public class Inj1 : SmartConventionInjection
    {
        protected override bool Match(SmartConventionInfo c)
        {
            return c.SourceProp.Name == c.TargetProp.Name;
        }
    }

    public abstract class SmartConventionInjection : ValueInjection
    {
        private class Path
        {
            public Type Source { get; set; }
            public Type Target { get; set; }
            public IDictionary<string, string> Pairs { get; set; }
        }

        protected abstract bool Match(SmartConventionInfo c);

        private static readonly IList<Path> paths = new List<Path>();
        private static readonly IDictionary<Type, Type> wasLearned = new Dictionary<Type, Type>();

        private Path Learn(object source, object target)
        {
            Path path = null;
            var sourceProps = source.GetProps();
            var targetProps = target.GetProps();
            var sci = new SmartConventionInfo
            {
                SourceType = source.GetType(),
                TargetType = target.GetType()
            };

            for (var i = 0; i < sourceProps.Count(); i++)
            {
                var s = sourceProps[i];
                sci.SourceProp = s;

                for (var j = 0; j < targetProps.Count(); j++)
                {
                    var t = targetProps[j];
                    sci.TargetProp = t;

                    if (!Match(sci)) continue;
                    if (path == null)
                        path = new Path
                        {
                            Source = sci.SourceType,
                            Target = sci.TargetType,
                            Pairs = new Dictionary<string, string> { { sci.SourceProp.Name, sci.TargetProp.Name } }
                        };
                    else path.Pairs.Add(sci.SourceProp.Name, sci.TargetProp.Name);
                }
            }
            return path;
        }

        protected override void Inject(object source, object target)
        {
            var sourceProps = source.GetProps();
            var targetProps = target.GetProps();

            if (!wasLearned.Contains(new KeyValuePair<Type, Type>(source.GetType(), target.GetType())))
            {
                lock (wasLearned)
                {
                    if (!wasLearned.Contains(new KeyValuePair<Type, Type>(source.GetType(), target.GetType())))
                    {

                        var match = Learn(source, target);
                        wasLearned.Add(source.GetType(), target.GetType());
                        if (match != null) paths.Add(match);
                    }
                }
            }

            var path = paths.SingleOrDefault(o => o.Source == source.GetType() && o.Target == target.GetType());

            if (path == null) return;

            foreach (var pair in path.Pairs)
            {
                var sp = sourceProps.GetByName(pair.Key);
                var tp = targetProps.GetByName(pair.Value);
                var setValue = true;
                var val = SetValue(ref setValue, new SmartValueInfo { Source = source, Target = target, SourceProp = sp, TargetProp = tp, SourcePropValue = sp.GetValue(source) });
                if (setValue) tp.SetValue(target, val);
            }
        }

        protected virtual object SetValue(ref bool setValue, SmartValueInfo info)
        {
            return info.SourcePropValue;
        }
    }

    public class SmartValueInfo
    {
        public PropertyInfo SourceProp { get; set; }
        public PropertyInfo TargetProp { get; set; }
        public object Source { get; set; }
        public object Target { get; set; }
        public object SourcePropValue { get; set; }
    }

    public class SmartConventionInfo
    {
        public Type SourceType { get; set; }
        public Type TargetType { get; set; }

        public PropertyInfo SourceProp { get; set; }
        public PropertyInfo TargetProp { get; set; }
    }
}