Reputation: 23
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
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);
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 List<>
has to be cast to IEnumerable<>
to be able to call FirstOrDefault
.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