2

is that possible to automate these mappings with reflection?

i have a simple column mapping :

var columnsMap = new Dictionary<string, Expression<Func<Industry, object>>>
{
    ["id"] = v => v.Id,
    ["name"] = v => v.Name,
    ["isActive"] = v => v.IsActive
};

columnsMap run-time result:

enter image description here


i want to automate these mappings in a wrapper class:

 public class QueryColumnMapper<T> : Dictionary<string, Expression<Func<T, object>>>
    {
        public QueryColumnMapper<T> GenerateMappings()
        {
            foreach (var item in typeof(T).GetProperties())
            {
                // get dictionary key ======> its OK
                var name = Char.ToLowerInvariant(item.Name[0]) + item.Name.Substring(1); //camel-case name

                // get expression    =======> this is the problem. wrong way
                Expression<Func<T, object>> exp = v => v.GetType().GetProperty(item.Name);
                //Expression<Func<T, object>> exp = v => v.?????;    <-----   

                // add to mapper object
                this.Add(name, exp);
            }
            return this;
        }
    }

example of using this class:

var columnsMap = new QueryColumnMapper<Industry>().GenerateMappings();  

columnsMap run-time result: enter image description here

i don't know is that possible to get my expression dynamically in run-time?

( i'm using this mapping dictionary for apply filtering on entity-framework IQueryable query. with first example (manual mapping) its working but i don't know how to do that in run-time without manual mapping )

AliReza Sabouri
  • 4,355
  • 2
  • 25
  • 37

2 Answers2

2

You can build a custom expression manually using the property name

// x =>
var parameter = Expression.Parameter(typeof(T));
// x.Name
var mapProperty = Expression.Property(parameter, "Name");
// (object)x.Name
var convertedExpression = Expression.Convert(mapProperty, typeof(object));
// x => (object)x.Name
var exp = Expression.Lambda<Func<T, object>>(convertedExpression, parameter);

If this is a common subset and you find you're doing it quite often you can create a base interface.

johnny 5
  • 19,893
  • 50
  • 121
  • 195
2

for other viewers i shared final code :

 public class QueryColumnMapper<T> 
{
    public QueryColumnMapper()
    {
        Mappings = new Dictionary<string, Expression<Func<T, object>>>();
    }
    public Dictionary<string, Expression<Func<T, object>>> Mappings { get; set; }
    public Dictionary<string, Expression<Func<T, object>>> GenerateMappings()
    {
        foreach (var item in typeof(T).GetProperties())
        {
            var name = Char.ToLowerInvariant(item.Name[0]) + item.Name.Substring(1); //camel-case name

            // add to mapper object
            Mappings.Add(name, GetExpression(item.Name));
        }
        return Mappings;
    }

    private Expression<Func<T,object>> GetExpression(string propertyName)
    {
        // x =>
        var parameter = Expression.Parameter(typeof(T));
        // x.Name
        var mapProperty = Expression.Property(parameter, propertyName);
        // (object)x.Name
        var convertedExpression = Expression.Convert(mapProperty, typeof(object));
        // x => (object)x.Name
        return Expression.Lambda<Func<T, object>>(convertedExpression, parameter);
    }
}

usage:

var columnsMap = new QueryColumnMapper<Industry>().GenerateMappings();
AliReza Sabouri
  • 4,355
  • 2
  • 25
  • 37
  • I'm glad you got it work +1 for showing the full code, but just a piece of advice, instead inheriting from dictionary you might just want a class which contains a dictionary instead – johnny 5 Jul 19 '18 at 15:08
  • can i ask the reason of that ? i inherited dictionary because i am using that class on other parts of service. (like ApplyFiltering method) – AliReza Sabouri Jul 19 '18 at 15:14
  • 1
    In general it's not good to inherit from Collections unless you need to expand the functionality of the collection, this [post](https://stackoverflow.com/questions/21692193/why-not-inherit-from-listt) will explain why – johnny 5 Jul 19 '18 at 15:15
  • @johnny5 ok i updated final code based on your advice. but now i wonder why i'm using a class here :) i just need that function.! with these changes i'm not using this class at all. should i return class instance in GenerateMappings method? or its the right way? – AliReza Sabouri Jul 19 '18 at 15:34
  • 1
    You could send the map back directly, but eventually you may find that you need additional metadata than just that map, so it might be nice to leave it in a class, but that's up to you, e.g you might want to add convenience methods for adding expressions or you may want to hide the implementation of the map and just off an apply function. – johnny 5 Jul 19 '18 at 15:36