C# LINQ - selecting a dynamic object based on properties defined at runtime

c# dynamic entity-framework-core expression-trees linq

Question

I've been trying to create an expression which can project a strongly typed EF Core entity into a dynamic object containing a list which are defined at runtime using a REST API call.

This is what I have so far:

Expression<Func<Message, dynamic>> DynamicFields(IEnumerable<string> fields)
{
    var xParameter = Expression.Parameter(typeof(Message), "o");
    var xNew = Expression.New(typeof(ExpandoObject));

    var bindings = fields.Select(o => {
        var mi = typeof(Message).GetProperty(o);
        var xOriginal = Expression.Property(xParameter, mi);
        return Expression.Bind(mi, xOriginal);
    });

    var xInit = Expression.MemberInit((dynamic)xNew, bindings);

    return Expression.Lambda<Func<Message, dynamic>>(xInit, xParameter);
}

It feels like I'm super close, but this bombs out at runtime stating that X property is not a member of ExpandoObject. I've tried changing up the use of the dynamic and ExpandoObject, but nothing seems to work - is this even possible?

If I switch out dyanmic / ExpandoObject for Message, it works just fine, but returns an instance of the Message class with all of its properties at their default values.

Anyone done this before?

Cheers.

1
1
9/7/2018 7:13:55 PM

Popular Answer

It's not possible to project to ExpandoObject / dynamic directly.

But you can project to dynamically created types at runtime. See for instance Microsoft.EntityFrameworkCore.DynamicLinq package - Dynamic Data Classes.

If you install that package form nuget (you can develop similar functionality yourself, but you should basically implement all that part of the package is doing), the method in question can be implemented as follows:

using System.Linq.Dynamic.Core;

static Expression<Func<TSource, dynamic>> DynamicFields<TSource>(IEnumerable<string> fields)
{
    var source = Expression.Parameter(typeof(TSource), "o");
    var properties = fields
        .Select(f => typeof(TSource).GetProperty(f))
        .Select(p => new DynamicProperty(p.Name, p.PropertyType))
        .ToList();
    var resultType = DynamicClassFactory.CreateType(properties, false);
    var bindings = properties.Select(p => Expression.Bind(resultType.GetProperty(p.Name), Expression.Property(source, p.Name)));
    var result = Expression.MemberInit(Expression.New(resultType), bindings);
    return Expression.Lambda<Func<TSource, dynamic>>(result, source);
}
2
9/7/2018 7:12:47 PM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow