jeudi 25 juin 2020

Build Expression tree to Add elements to Collection dynamically c#

I have a class and I need to iterate tru each property reading the attribute name to map to my data Source the value, in the cases where I have a ICollection that property will have multiple attributes to map the correct value.

I'm using Expression trees to set the values efficiently to each property but I'm having issues to set the values to the Collection. I think this is because I need to create an instance of that Collection but I don't know. I'm a bit lost on that one here's what I got:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class MapToAttribute : Attribute
{
    public MapToAttribute(string field)
    {
        Field = field;
    }

    public string Field { get; private set; }
}

public class MyDataClass
{
    [MapTo("one")]
    public int propOne { get; set; }

    [MapTo("two")]
    public string propTwo { get; set; }

    [MapTo("item1")]
    [MapTo("item2")]
    [MapTo("item3")]
    public ICollection<int> collection { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var setter = SetValues<MyDataClass>();
    }

    private static IEnumerable<T> SetValues<T>()
        where T : new()
    {
        var properties = GetClassProperties<T>();
        var results = new List<T>();

        for (int x = 1; x<=100; x++)
        {
            var row = new T();

            //Simulated Datasource
            var dataSource = new Dictionary<string, object>();
            dataSource.Add("one", x);
            dataSource.Add("two", x.ToString());
            dataSource.Add("item1", x);
            dataSource.Add("item2", x+x);
            dataSource.Add("item3", x*x); 

            foreach (var property in properties)
            {
                //this line executes the Action
                property.Value(row, dataSource[property.Key]);
            }
            results.Add(row);
        }
        return results;
    }

    private static Dictionary<string, Action<T, object>> GetClassProperties<T>()
    {
        var setters = new Dictionary<string, Action<T, object>>();

        var instance = Expression.Parameter(typeof(T));
        var argument = Expression.Parameter(typeof(object));

        foreach (var property in typeof(T).GetProperties())
        {
            var names = property.GetCustomAttributes(typeof(MapToAttribute), true)
                .Select(p => ((MapToAttribute)p).Field);

            var setter = Expression.Lambda<Action<T, object>>(
                  Expression.Call(
                        instance,
                        property.GetSetMethod(),
                        Expression.Convert(argument, property.PropertyType)
                  ), instance, argument
                ).Compile();

          // Due to the types I cannot just assign a value to a ICollection,
          // that's why I tried to create HERE a different setter 
          // when the property Type is ICollection, I commented out the code failing.
          
            //var getCollection = Expression.Lambda<Func<T, object>>(
            //        Expression.Call(
            //            instance,
            //            prop.GetGetMethod()
            //        ), instance
            //      ).Compile();

            //Action<T, object> setter = (classInstance, value) =>  
            // getCollection(classInstance).Add(value);


            foreach (var name in names)
            {
                setters.Add(name, setter);
            } 
        }

        return setters;
    }
}




Aucun commentaire:

Enregistrer un commentaire