tic
tic

Reputation: 2512

Create select within select in expression tree

How can I create the following with an expression tree? Assume claims is an IQueryable of ClaimData

var lastNames = claims.Select(p1 => p1.Advisors.Select(p2 => p2.LastName));

With the example classes as follows:

public class ClaimData
{
    public string name { get; set; }
    public IQueryable<AdvisorData> Advisors { get; set; }
    public IQueryable<ActionData> Actions { get; set; }
}

public class AdvisorData
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class ActionData
{
    public string Name { get; set; }
    public string Comment { get; set; }
}

Assuming we don't know which property of ClaimData (Advisors in this example) or which property of that (LastName in this example) we want to Select.

I would like to create the selector but am having difficulty. Here is code that I have attempted, but is incomplete and I am clearly getting confused

var property = typeof(ClaimData).GetProperties().Where(p =>
{
    var args = p.PropertyType.GetGenericArguments();
    if (args.Count() == 0) return false;
    var innerProperty = args.First().GetProperties().Where(pi => PropertyByAttributeFunc(pi, table, column));
    return innerProperty.Count() > 0;
}).First();

var parameterExp = Expression.Parameter(typeof(ClaimData), "p1");
var propertyExp = Expression.Property(parameterExp, property.Name);

var propertyType = property.PropertyType.GenericTypeArguments.First();
var parameterInnerExp = Expression.Parameter(propertyType, "p2");
var propertyInner = propertyType.GetProperties().Where(pi => PropertyByAttributeFunc(pi, table, column)).First();
var propertyInnerExp = Expression.Property(parameterInnerExp, propertyInner.Name);

var selectMethod = typeof(Queryable).GetMethods().Where(x => x.Name == "Select").First()
.MakeGenericMethod(property.PropertyType, typeof(string));

var genericFunc = typeof(Func<,>).MakeGenericType(property.PropertyType, typeof(string));
var genericInnerFunc = typeof(Func<,>).MakeGenericType(propertyType, typeof(string));

var innerLambda = Expression.Lambda(genericInnerFunc, propertyInnerExp, parameterInnerExp);

var expCall = Expression.Call(selectMethod, propertyExp, innerLambda);

Don't worry about this line:

var propertyInner = propertyType.GetProperties().Where(pi => PropertyByAttributeFunc(pi, table, column)).First();

It just returns the correct property for me.

Thanks

Upvotes: 0

Views: 393

Answers (1)

Servy
Servy

Reputation: 203835

Your error was on the following line:

var selectMethod = typeof(Queryable).GetMethods()
    .Where(x => x.Name == "Select").First()
    .MakeGenericMethod(property.PropertyType, typeof(string));

property.PropertyType is IQueryable<AdvisorData>. What it should be is just AdvisorData. The first generic argument of Select is looking for the type of one item in the query, rather than the type of the entire IQueryable. This means your Select call needs to pass in an IQueryable<IQueryable<AdvisorData>> to match the parameter Select expects.

The change is simple enough:

var selectMethod = typeof(Queryable).GetMethods()
    .Where(x => x.Name == "Select").First()
    .MakeGenericMethod(property.PropertyType.GetGenericArguments()[0], 
        typeof(string));

Upvotes: 1

Related Questions