I am playing around with expression trees and trying to better understand how they work. I wrote some sample code that I'm working with and hopefully someone can help me out.
So I have this somewhat messy query:
/// <summary>
/// Retrieves the total number of messages for the user.
/// </summary>
/// <param name="username">The name of the user.</param>
/// <param name="sent">True if retrieving the number of messages sent.</param>
/// <returns>The total number of messages.</returns>
public int GetMessageCountBy_Username(string username, bool sent)
{
var query = _dataContext.Messages
.Where(x => (sent ? x.Sender.ToLower() : x.Recipient.ToLower()) == username.ToLower())
.Count();
return query;
}
_dataContext
is the entity framework data context. This query works beautifully, but it's not easy to read. I decided to factor the inline IF statement out into a Func
like this:
public int GetMessageCountBy_Username(string username, bool sent)
{
Func<Message, string> userSelector = x => sent ? x.Sender : x.Recipient;
var query = _dataContext.Messages
.Where(x => userSelector(x).ToLower() == username.ToLower())
.Count();
return query;
}
This seems like it would work great, but there is a problem. Because the query is against IQueryable<T>
this LINQ expression is being translated into SQL to be executed at the data source. That's great, but because of this it does not know what to do with the call to userSelector(x)
and throws an exception. It cannot translate this delegate into an expression.
So now that I understand why it's failing I would like to try and make it work. It's far more work for what I need, but I'm doing it just out of pure interest. How might I turn this Func
into an expression that can be translated into SQL?
I tried to do this:
Expression<Func<Message, string>> userSelectorExpression = x => sent ? x.Sender : x.Recipient;
Func<Message, string> userSelector = userSelectorExpression.Compile();
With this however, I get the same error. I think I'm failing to understand expressions. I think all I'm doing with the above code is writing an expression but then turning it into executable code again and then getting the same error. However, if I try to use userSelectorExpression
within the LINQ query it can't be called like a method.
Edit
For those interested in the exception, here it is:
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
I took this to mean that it could not "invoke" the userSelector
delegate. Because, as stated above, it needs to translate it into an expression tree.
When using a real method, you get a slightly more verbose error message:
LINQ to Entities does not recognize the method 'System.String userSelector(Message, Boolean)' method, and this method cannot be translated into a store expression.