Question

I want to create a query of type Expression that gets some columns of an entity from Entity Framework.

Assume we have two classes like this:

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Child MyChild { get; set; }
}

public class Child
{
    public int Id { get; set; }
    public string Name { get; set; }
}

And we have an IQueryable list of Parent:

var q = new List<Parent>()
{
    new Parent {Id = 1, Name = "a", Number = 1, MyChild=new Child{Id=11,Name="Child_a",Number=2}},
    new Parent {Id = 2, Name = "b", Number = 1, MyChild=new Child{Id=22,Name="Child_b",Number=2}},
    new Parent {Id = 3, Name = "c", Number = 1, MyChild=new Child{Id=33,Name="Child_c",Number=2}},
}.AsQueryable();

I want to get a list of those properties of q that user determines them. For example user determines that he needs Parent.Name and Parent.MyChils.Name. So I should give the user a list of anonymous type like this:

{"a","Child_a"}
{"b","Child_b"}
{"c","Child_c"} 

If the Parent entity doesn't contain any foreign key property (in this example MyChild property) it is so easy to create an expression property that takes some properties of Parent dynamically. I have a code that gets some properties of Person without MyChild property of it:

var columns = new List<string> { "Id", "Name" };
var xParam = Expression.Parameter(typeof(Parent), "x");
var sourceProperties = columns.ToDictionary(name => name,
    name => q.ElementType.GetProperty(name));
var dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);
var bindings =
    dynamicType.GetFields()
        .Select(p => Expression.Bind(p, Expression.Property(xParam, sourceProperties[p.Name])))
        .OfType<MemberBinding>();
var newExpr = Expression.New(dynamicType.GetConstructor(Type.EmptyTypes));
Expression selector = Expression.Lambda(Expression.MemberInit(
    Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), xParam);
var body = Expression.MemberInit(newExpr, bindings);
var lambda = Expression.Lambda<Func<Parent, dynamic>>(body, xParam);
var t = q.Select(lambda);

(2 used methods are here:)

public static Type GetDynamicType2(Dictionary<string, Type> fields)
{
    if (null == fields)
        throw new ArgumentNullException("fields");
    if (0 == fields.Count)
        throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition");
    try
    {
        Monitor.Enter(builtTypes);
        string className = "MyDynamicType";
        if (builtTypes.ContainsKey(className))
            return builtTypes[className];
        TypeBuilder typeBuilder = moduleBuilder.DefineType(className,
            TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);
        foreach (var field in fields)
            typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public);
        builtTypes[className] = typeBuilder.CreateType();
        return builtTypes[className];
    }
    catch (Exception ex)
    {
    }
    finally
    {
        Monitor.Exit(builtTypes);
    }

    return null;
}

public static Type GetDynamicType(IEnumerable<PropertyInfo> fields)
{
    return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType));
}

But still I can't get internal properties of MyChild property of Parent.

How to do that?

Was it helpful?

Solution

Since nobody answered my question I tried many different ways to solve it, upshot problem solved utilizing recursive creation of Expression. For every property of type Class (like MyChild in the question) we most create an Expression. Creation of Expressions most be recursive like this:

    private Expression BuildExpression(Expression parentExpression,
        Type parentPropertyType, string pathOfChildProperty)
    {
        string remainPathOfChild;
        var childPropertyName = 
              GetFirstPropertyNameFromPathAndRemainPath(pathOfChildProperty, out remainPathOfChild);
        if (string.IsNullOrEmpty(childPropertyName)) return parentExpression;
        var childPropInfo = parentPropertyType.GetProperty(childPropertyName);
        var childExpression = Expression.Property(parentExpression, childPropInfo);
        return !string.IsNullOrEmpty(remainPathOfChild)
            ? BuildExpressionForInternalProperty(childExpression, childPropInfo.PropertyType, remainPathOfChild)
            : childExpression;
    }
    private string GetFirstPropertyNameFromPathAndRemainPath(string path, out string remainPath)
    {
        if (string.IsNullOrEmpty(path))
        {
            remainPath = null;
            return null;
        }
        var indexOfDot = path.IndexOf('.');
        if (indexOfDot < 0)
        {
            remainPath = null;
            return path;
        }
        remainPath = path.Substring(indexOfDot + 1);
        return path.Substring(0, indexOfDot);
    }
    }

Recursive call will stop when remain path of internal property be empty. In highest level the calling of this method is like this:

var inputParameter = Expression.Parameter(typeof(GrandParent), "x");
var expChildProperty = 
    BuildExpression(inputParameter,typeof(Parent),"Parent.MyChils.Name");

Finally result expression for above problem is (($x.Parent).MyChils).Name in debug view.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top