Function application in LINQ expressions
We are trying to find a workaround for the issue that the Entity Framework doesn't support non-scalar entities. We are using a particular equality so we try to build an expression that for a given input and a function from that input checks whether that equality holds.
private static Expression<Func<TElement, bool>> UserEquals<TElement>(User user, Func<TElement, User> select)
{
var userequals = (Expression<Func<User, Boolean>>) (u => u.Source == user.Source && u.UserName == user.UserName);
//return an Expression that receives an TElement, applies |select| and then passes that result to then `userequals` expression
// and uses it's result as return value.
}
I suspect it involves creating a new expression that receives a parameter, but I cannot figure out how to apply the select
function to that input and then pass the result of that on to the userequals
expression.
The intended usage is something like:
Context.Foo.Where(UserEquals(user, (f => f.User)).Single(f => f.Id == id);
Instead of:
Context.Foo.Single(f => f.Id == id && f.User.Source == user.Source && f.User.UserName == user.UserName);
Ideally we w开发者_如何学JAVAould want to write something like:
Context.Foo.Single(f => f.Id == id && f.User.Equals(user))
// or
Context.Foo.Single(f => f.Id == id && f.User == user)
So, if I'm understanding you correctly you want to do this:
public class Foo
{
public int Id { get; set; }
public User User { get; set; }
}
public class User
{
public int Id { get; set; }
public string Text { get; set; }
}
public static IQueryable<Foo> WhereUserEquals(this IQueryable<Foo> source, User user)
{
// this is your implementation of the entity specific equality test
return source.Where(x => x.User.Id == user.Id);
}
static void Main(string[] args)
{
var list = new List<Foo> { new Foo { User = new User { Id = 1, Text = "User" } };
var user = new User { Id = 1 };
var q = list.AsQueryable().WhereUserEquals(user);
foreach (var item in q)
{
Console.WriteLine(item.Text);
}
}
Which would allow you to write:
Context.Foo.WhereUserEquals(user).Single(f => f.Id == id);
If you don't have a base class for accessing the User property of the type Foo you need one such extension for each type, however, that's something which you could quite easily code gen. I don't believe expression tree rewriting will be necessary.
Are you by any chance looking for InvokeExpression?
Could this work ?
private static Expression<Func<TElement, bool>> UserEquals<TElement>(User user, Expression<Func<TElement, User>> select)
{
return (Expression<Func<TElement, Boolean>>)(elt => select.Compile()(elt).Source == user.Source && select.Compile()(elt).UserName == user.UserName);
}
Currently you have a type mismatch since select is invoked nowhere, and thus you have not a TElement
as input but already an User
.
Hope this helps...
This library solves the problem: http://nuget.org/List/Packages/Microsoft.Linq.Translations
Read more about it here: http://damieng.com/blog/2009/06/24/client-side-properties-and-any-remote-linq-provider
I'm relying heavily on this in production.
精彩评论