th3monk3y
th3monk3y

Reputation: 23

Need help converting a lambda to an expression tree

I've been playing with this for hours and can use a fresh set of eyes. Okay someone with more experience than me concerning expression trees. Man the curve is steep!

consider the following. (which works, but I need Accounts to be passed in as a string. Accounts is list of Account)

repo = repo.Where(x => x.Accounts.FirstOrDefault().Id == AccountId);

This is what I have so far.

            var parameterExp = Expression.Parameter(typeof(Boat), "x");
            var propertyExp = Expression.Property(parameterExp, "Accounts");

            MethodInfo method1 = typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static).First(m => m.Name == "FirstOrDefault");

            // This gives me a Queryable of Account FirstOrDefault
            var specificMethod = method1.MakeGenericMethod(propertyExp.Type);

            // right here is where I am getting stuck
            var firstOrDefaultAccountExpression = // I need to get an Account so I can query against the Id ??


            MethodInfo method = typeof(long).GetMethod("Equals", new[] { typeof(long) });
            var someValue = Expression.Constant(AccountId, typeof(long));
            var containsMethodExp = Expression.Call(firstOrDefaultAccountExpression , method, someValue);

            Expression<Func<Boat, bool>> predicate = Expression.Lambda<Func<Boat, bool>>
                         (containsMethodExp, parameterExp);


            repo = repo.Where(predicate);

I was able to get this code to work on a regular string member. I just can't seem to figure out the list. Ultimately I am trying to get back a list of Boat where AccountId = long

Thanks in advance!

Upvotes: 1

Views: 215

Answers (1)

Anton&#237;n Lejsek
Anton&#237;n Lejsek

Reputation: 6103

var parameterExp = Expression.Parameter(typeof(Boat), "x");
Expression propertyExp = Expression.Property(parameterExp, "Accounts");

Type elementType = propertyExp.Type.GetGenericArguments()[0];

MethodInfo method1 = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static).First(m => m.Name == "FirstOrDefault");

// This gives me a Queryable of Account FirstOrDefault
var specificMethod = method1.MakeGenericMethod(elementType);

//propertyExp = Expression.TypeAs(propertyExp, typeof(IEnumerable<>).MakeGenericType(elementType));

var firstOrDefaultAccountExpression = Expression.Call(specificMethod, propertyExp);

var idExpr = Expression.PropertyOrField(firstOrDefaultAccountExpression, "Id");

MethodInfo method = typeof(long).GetMethod("Equals", new[] { typeof(long) });
var someValue = Expression.Constant(AccountId, typeof(long));
var containsMethodExp = Expression.Call(idExpr, method, someValue);

Expression<Func<Boat, bool>> predicate = Expression.Lambda<Func<Boat, bool>>
             (containsMethodExp, parameterExp);

repo = repo.Where(predicate);

List<> has to be cast to IEnumerable<> to be able to call FirstOrDefault. I would swear it did not work without the cast, but now it works, strange. So I commented out the line. But in both versions it is able to produce sql code in linq2sql, I tested that. You do not need Queryable for accessing Accounts property (and I am not sure if even can have). Look at the first line of your code, the non-expression query. Even there the FirstOrDefault is a method of Enumerable. Its purpose is to define the transformation by defining expression, it is not function that actually does anything.

Upvotes: 1

Related Questions