Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using LINQ expression to assign to an object's property

So I'm working with an old data model, and I kind of have to work within what I've been handed.

When I perform a database query, the model returns data as a

List<Dictionary<string, object>>

Where for each dictionary, the key is the column name and the value is the column value. As you can imagine, working with this is a nightmare of foreach loops and type casting

I'm hoping to define some POCO viewmodels and then making something that uses LINQ/reflection, and an "assignment binding map" to go from hideous return value to my nice clean POCO. So I could define "maps" with the column names and lambdas to the properties on my POCO, similar to this...

var Map; // type???
Map.Add("Id", p => p.Id);
Map.Add("Code", p => p.Code);
Map.Add("Description", p => p.Description);
Map.Add("Active", p => p.Active);

Then convert like this...

List<Dictionary<string, object>> Results = MyModel.Query(...);
List<ProductViewModel> POCOs = new List<ProductViewModel>();

foreach (var Result in Results) // Foreach row
{
  ProductViewModel POCO = new ProductViewModel();

  foreach (var i in Result) // Foreach column in this row
  {
    // This is where I need help.
    // i.Key is the string name of my column.
    // I can get the lambda for this property from my map using this column name.
    // For example, need to assign to POCO.Id using the lambda expression p => p.Id
    // Or, assign to POCO.Code using the lambda expression p => p.Code
  }

  POCOs.Add(POCO);
}

return POCOs;

Can this be done using some sort of reflection, and if so, how?

like image 589
Stevoman Avatar asked Nov 28 '11 21:11

Stevoman


2 Answers

Here is an approach using expression trees. First, define the API of the map:

public class PropertyMap<T> where T : new()
{
    public void Add(string sourceName, Expression<Func<T, object>> getProperty);

    public T CreateObject(IDictionary<string, object> values);
}

You would use it like this:

var map = new PropertyMap<ProductViewModel>();

map.Add("Id", p => p.Id);
map.Add("Code", p => p.Code);
map.Add("Description", p => p.Description);
map.Add("Active", p => p.Active);

var productViewModel = map.CreateObject(values);

To implement it, first you would declare a dictionary to associate names from the data source to properties:

private readonly IDictionary<string, PropertyInfo> _properties = new Dictionary<string, PropertyInfo>();

Next, you would implement the Add method in terms of that dictionary (all error handling left as an exercise for the reader):

public void Add(string sourceName, Expression<Func<T, object>> getProperty)
{
    _properties[sourceName] = (PropertyInfo) ((MemberExpression) getProperty.Body).Member;
}

Then, you would dynamically compile a method, using expression trees, which does the assignments (it sounds scarier than it is). The easiest way to visualize this process is to look at an example of what we're building. What we want is some code which does this:

new ProductViewModel
{
    Id = ...,
    Code = ...,
    Description = ...,
    Active = ...
}

But, we can't know that at compile-time because of the dynamic mappings. So, we'll build a function which is that exact code, but compiled at runtime. Expression trees are just runtime data that represents the same code you could write at compile-time.

First, we need to get a set of bindings (assignments) for the properties:

private IEnumerable<MemberBinding> GetPropertyBindings(IDictionary<string, object> values)
{
    return
        from sourceName in _properties.Keys
        select Expression.Bind(_properties[sourceName], Expression.Constant(values[sourceName]));
}

What we're saying here is, for each property in the mapped properties, look up the value and make it a constant (for Id, this might be the value 7) and bind the corresponding property to it. This gives us the expression Id = 7. We repeat this for all of the properties, giving us all of the assignments.

Once we have those bindings, we can create the full member initialization, which includes the constructor call:

private MemberInitExpression GetMemberInit(IDictionary<string, object> values)
{
    return Expression.MemberInit(Expression.New(typeof(T)), GetPropertyBindings(values));
}

Because we specified where T : new() in the class declaration, we are guaranteed to have a parameterless constructor to call here. We pass in the property bindings we created before, giving us a data structure that represents the initialization expression we wanted to build.

So what do we do know? We have this data structure, but how do we call the code? To do that, we have to wrap that expression in a function that we can call, because the only thing you can actually invoke is a method. This means we are really building code that looks like this:

() => new ProductViewModel
{
    Id = ...,
    Code = ...,
    Description = ...,
    Active = ...
}

That is a parameterless function which, when invoked, will return the initialized object. This is also called a lambda expression. We can get the data structure for this like so:

private Func<T> GetInitializationFunction(IDictionary<string, object> values)
{
    var initializationLambda = Expression.Lambda<Func<T>>(GetMemberInit(values));

    return initializationLambda.Compile();
}

We create a lambda expression whose body is the member initialization, which is exactly the code we wrote above. We specify the delegate type Func<T> because it takes no parameters and returns an object of the mapped type.

Then, we compile it. This call generates a method with the signature Func<T> that we can call, and which has as its body the code we created as a data structure. This is a neat way of doing reflection without using reflection directly.

Finally, we implement the CreateObject method we defined earlier by creating the function and invoking it, giving us an instance of T (ProductViewModel here):

public T CreateObject(IDictionary<string, object> values)
{
    var initializationFunction = GetInitializationFunction(values);

    return initializationFunction();
}
like image 171
Bryan Watts Avatar answered Nov 02 '22 04:11

Bryan Watts


What you could do is to extract the property name from a linq expression of the kind p => p.Id using something like this

public static string GetPropertyName<T>(Expression<Func<T>> expression)
{
    MemberExpression body = (MemberExpression)expression.Body;
    return body.Member.Name;
}

..and then use plain old reflection to actually assign the value to the object instance. For instance create a method

private void Assign(object objInstance, Expression<Func<T>> propertyExpression, object value)
{
    string propertyNameToAssign = GetPropertyName(propertyExpression);

    //TODO use reflection to assign "value" to the property "propertyNameToAssign" of "objInstance"
}

(didn't compile the code; for the reflection part, there are numerous articles on the web. Hope this helps)

like image 32
Juri Avatar answered Nov 02 '22 03:11

Juri