I have a feeling I already know the answer to this question.
I have an expression that common but complex, and I would like to re-use the code in multiple locations. I would like to use a function that returns a Func with some code:
public static Func<MyClass, bool> GetCheck()
{
return (x) => x.Value > 10;
}
Seems easy. The problem that I'm having is when I apply it to a LINQ expression that's used in LINQ To Entities. I get an error that says Invoke is not supported. And I understand why. What I don't know is if there's a way that I can get around this. I would like to say...
var check = GetCheck();
IQueryable<MyClass> results = MyClasses.Where(y => check(y));
... and have the expression tree inspector realize that everything that happens in the Func is perfectly legal both in LINQ To Entities and on the DB. It seems like it should be inline'd.
Is there anything I can do to make this happen? Any form of declaration for the delegate that will allow this?
Since you are querying an IQueryable<> then you should try this:
public static Expression<Func<MyClass, bool>> GetCheck()
{
return (x) => x.Value > 10;
}
Related
I would like to do following thing in Entity Framework code first:
Data.GroupBy(x => x.Person.LastName)
.Select(x => x.OtherGrouping(...)).Where(...).GroupBy(...)...etc
public static class Extensions
{
public static IQueryable<IGrouping<T, T>> OtherGrouping<T>(this IQueryable<T> collection /**** Here I want to pass consts and lambdas\expressions*****/)
{
// Grouping, filtration and other stuff here
return collection.GroupBy(x => x);
}
}
The problem is .net compiles OtherGrouping method, it doesn't represent like an expression, and couldn't be transformed to the SQL.
I found LinqKit library which suppose to help in such cases, but I coudn't figure out how to apply it in my specific case. It works fine for simple cases, when I just have expression like x => x+2, but I get stucked with return IQueryable. Probably it is possible to write expression tree completely by hand but I don't want to go so deep.
Any ideas how it might be done or where I can read about it?
Here is what I tried to do based on Rob's comment
void Main()
{
AddressBases.GroupBy(x => x.ParentId).Select(x => new {x.Key, Items = x.AsQueryable().OtherGrouping(a => a.Country) }).Dump();
}
public static class Extensions
{
public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
source.GroupBy(keySelector).Expression);
}
}
And I got an exception:
NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[System.Linq.IGrouping`2[System.String,InsuranceData.EF.AddressBase]] OtherGrouping[AddressBase,String](System.Linq.IQueryable`1[InsuranceData.EF.AddressBase], System.Linq.Expressions.Expression`1[System.Func`2[InsuranceData.EF.AddressBase,System.String]])' method, and this method cannot be translated into a store expression.
Couldn't figure out yet why I have OtherGrouping method in expression tree? OtherGrouping method should just attach another grouping to the expression and pass it to the provider, but not put itself to the tree.
Your current code after the edit is correct - where you're going wrong is a common issue with the syntactic sugar we're given by C#.
If you write the following:
void Main()
{
var res = Containers.OtherGrouping(c => c.ContainerID);
res.Expression.Dump();
res.Dump();
}
public static class Extensions
{
public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
source.GroupBy(keySelector).Expression
);
}
}
You'll see that we get the output:
Table(Container).GroupBy(c => c.ContainerID)
And the result executes with an issue, properly grouping by our predicate. However, you're invoking OtherGrouping while inside an expression, when you're passing it to Select. Let's try a simple case without OtherGrouping:
var res = Containers.OtherGrouping(c => c.ContainerID)
.Select(x => new
{
x.Key,
Items = x.GroupBy(c => c.ContainerID),
});
res.Expression.Dump();
What you're providing to Select is an expression tree. That is, the inner GroupBy's method is never actually invoked. If you change the query to x.AsQueryable().OtherGrouping and put a breakpoint in OtherGrouping, it will only be hit the first time.
In addition to that, x is IEnumerable<T>, not IQueryable<T>. Invoking AsQueryable() gives you an IQueryable, but it also gives you a new query provider. I'm not 100% sure as to the inner workings of entity framework, but I'd wager to say that this is invalid - as we no longer have the database as a target for the provider. Indeed, with linq2sql (using LINQPad), we get an error saying .AsQueryable() is not supported.
So, what can you do? Unfortunately there's no clean way to do this. Since the expression tree is already built before OtherGrouping has a chance, it doesn't matter what the body of OtherGrouping is. We'll need to change the expression tree after it's built, but before it's executed.
For that, you'll need to write an expression visitor which will look for .OtherGrouping expression calls, and replace it with Queryable.GroupBy
I have the following expression:
public Expression<Func<T, bool>> UserAccessCheckExpression<T>(int userId) where T : class
{
return x => (IsAdmin || userId == CurrentUserId || userId == 0);
}
Then I want to apply this filter to several collections (IQueryable) like this one:
return tasks
.Where(t => t.TaskUsers
.Any(x => UserAccessCheckExpression<TaskUser>(x.User) && x.SomeBool == true));
I'm getting the following error while doing so:
Error 40 Cannot implicitly convert type System.Linq.Expressions.Expression<System.Func<TaskUser,bool>> to bool
I can't use workaround with interface inheritance (like TaskUser inherits interface with int UserId property (where T : IHasUserId)) since I want to combine logic.
The problem is that your UserAccessCheckExpression() method is returning an Expression while the Any() method is expecting a boolean.
Now, you can get your code to compile by compiling the Expression and invoking the method (using UserAccessCheckExpression<TaskUser>(x.User).Compile().Invoke(x.User)) but that would obviously fail on runtime because Linq-to-Entities wouldn't be able to translate your Any() to a store query as it no longer contains an Expression.
LinqKit is aiming to solve this problem using its own Invoke extension method that while letting your code compile, will make sure your Expression will get replaced back to its original form using another extension method named AsExpandable() that is extending the entity set.
Try this:
using LinqKit.Extensions;
return tasks
.AsExpandable()
.Where(t => t.TaskUsers.Any(
x => UserAccessCheckExpression<TaskUser>(x.User).Invoke(x)
&& x.SomeBool == true));
More on LinqKit
Yeah, so, you can't do that. There's a difference between an Expression<> and a Func<>. You're trying to use the UserAccessCheckExpression as a func. I'm not sure what you're trying to do, but you can compile it to a func and then use it sorta like you are:
var expr = UserAccessCheckExpression<TaskUser>(x.User);
var func = expr.Compile();
// Later use it like ...
var result = func();
But I expect you're using this with EF or Linq2Sql? That being the case you'll need to rewrite the expression. It can be done by hand (not easy) or, better, use a tool like PredicateBuilder.
Let say I have a function like this:
var filterValue = GetCurrentFilter(state);
And then an EF query:
var result = context.EntitySet.Where(x=> x.column > filterValue);
this works, but as soon as I try to inline that:
var result = context.EntitySet.Where(x=> x.column > GetCurrentFilter(state));
It does not because EF Linq tried to parse GetCurrentFilter into expression tree and is unable to do that. This is all quite understandable.
My question is, is there a way to let EF Linq know that in needs to execute the GetCurrentFilter function when it builds the tree and use its result in the tree?
Something like
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)));
Since GetCurrentFilter does not have parameters that is a part of the query this should be technically possible to do that if EF Linq can support it that is. I'm suspecting that I'm just missing the correct syntax for that.
Make GetCurrentFilter a (read only) property instead of a method. EF will evaluate properties to their values, rather than trying to translate them into SQL, unlike methods.
The only other road that you have is to traverse the entire expression tree, search for usage of your ResultOf method, evaluate its parameter to a value, and then inline that value where the ResultOf call once was, rebuiding the query around that value.
In order for this to work it means you need to not only wrap the code you want to inline in a call to EfUtil.ResultOf, but it also means calling a method on the query itself to force it to go back and evaluate it:
public class EfUtil
{
public static T ResultOf<T>(T value)
{
return value;
}
}
//Note this could probably use a better name
public static IQueryable<T> EvaluateResults<T>(this IQueryable<T> query)
{
return query.Provider.CreateQuery<T>(
new ExpressionEvaluator().Visit(query.Expression));
}
internal class ExpressionEvaluator : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.Name == "ResultOf" && m.Method.DeclaringType == typeof(EfUtil))
{
Expression target = m.Arguments[0];
object result = Expression.Lambda(target)
.Compile()
.DynamicInvoke();
return Expression.Constant(result, target.Type);
}
else
return base.VisitMethodCall(m);
}
}
This would allow you to write:
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)))
.EvaluateResults();
It would then evaluate GetCurrentFilter(state) on the client side and inline the result as a constant into the query.
As a slightly simpler test, we can write the following:
var query = new[] { 1, 2, 3 }
.AsQueryable()
.Where(x => x > EfUtil.ResultOf(Math.Max(1, 2)))
.EvaluateResults();
Console.WriteLine(query.ToString());
And it will print out:
System.Int32[].Where(x => (x > 2))
Which is exactly what we want.
Note that the use of the lambda's parameter (x in these examples) cannot be used anywhere within the call to EfUtil.ResultOf or the code won't work, and couldn't possibly be made to work (although we could generate a better error message if we cared enough).
I'm trying to DRY out some lambda expressions for security rights. Is it possible to take a lamda expression and apply it to a single entity for true?
Like lets say I have a Person and a DocumentFolder
Expression<Func<Person, bool>> CanSeePerson()
{
return c => !c.IsPrivate;
}
And one for the folder
Expression<Func<DocumentFolder, bool>> CanSeeFolder()
{
return c => !c.IsPrivate && c.Owner.CanSeePerson(); // <- ???
}
How the heck can I use that CanSeePerson() function on a single type to return true and maintain an expression that can be used in linq queries like such
Entities.DocumentFolder.Where(CanSeeFolder());
I know how to use the where on an iqueryable but I can't see how to apply the expression tree to a single value.
This throws the error: Unable to create a constant value of type 'Person'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Expression<Func<DocumentFolder, bool>> CanSeeFolder()
{
return c => !c.IsPrivate &&_entities.Persons.Where(x => x.Id == c.Owner.Id).Any(CanSeePerson());
}
The difference seems to be based on putting the IQueryable Directly in the statement. This also does NOT work
Expression<Func<DocumentFolder, bool>> CanSeeFolder()
{
return c => !c.IsPrivate &&_entities.Persons.Where(CanSeePerson()).Contains(c.User);
}
but this DOES work
Expression<Func<DocumentFolder, bool>> CanSeeFolder()
{
var canSeePersons = _entities.Persons.Where(CanSeePerson());
return c => !c.IsPrivate && canSeePersons.Contains(c.User);
}
p.s. I know I suck # using this stackoverflow formatting thing lol
The reason why this works is because the CanSeePerson() function cannot be converted and used in an expression. When you put the canSeePersons variable in the function instead you are placing in an iQueryable type which can be used in an expression. Using "var" convolutes it a bit.
You defined a Expression<Func<Person, bool>> that solves your problem. The trick is to simply transform your list of objects to the list you want to filter. It is not always possible, but many times it is. You just have to be a bit creative :-)
When using extension methods you can easily come up with a solution that allows you to do this:
var visibleFolders = Entities.DocumentFolder.WhereCanSeeFolder();
Here is the (completely DRY) solution:
public static class SecurityExtensions
{
public static IQueryable<DocumentFolder> WhereCanSeeFolder(
this IQueryable<DocumentFolder> folders)
{
var visibleOwners = folders.Select(f => f.Owner)
.Where(CanSeePerson);
return
from folder in folders.Where(CanSeeFolder)
where visibleOwners.Contains(folder.Owner)
select folder;
}
private static readonly Expression<Func<DocumentFolder, bool>>
CanSeeFolder = folder => !folder.IsPrivate;
private static readonly Expression<Func<Person, bool>>
CanSeePerson = person => !person.IsPrivate;
}
I hope this helps.
The expression tree is only applied to a single value at a time, logically, within the LINQ expression.
If you're saying you want to apply it in-process later to a single value, you can just use:
// This can be cached
Func<DocumentFolder, bool> canSeeFolderDelegate = CanSeeFolder().Compile();
DocumentFolder folder = ...; // Get the value from somewhere
if (canSeeFolderDelegate(folder))
{
// Yes, you can see that folder
}
Ah, you want to evaluate it for a single entity?
var func = CanSeeFolder().Compile(); // <=== store and re-use this;
// this isn't free
bool canSee = func(obj);
Another approach might be:
bool canSee = Enumerable.Repeat(obj, 1).AsQueryable().Any(CanSeeFolder());
but this is probably still going to do the Compile somewhere in the chain, so you may as well use the more direct code (at the top).
Edit re comments:
To evaluate that at the database, you would need a restriction, for example:
bool canSee = db.Folders.Where(f => f.FolderId == id)
.Where(CanSeeFolder()).Any();
which limits us to the single row, then adds your extra filter.
I would like to create a compiled query which uses reusable where predicates. An example to make this clear:
ObjectContext.Employees.Where(EmployeePredicates.CustomerPredicate)
EmployeePredicates is a static class with a property CustomerPredicate that looks like this:
public static Expression<Func<Employee, bool>> CustomerPredicate
{
get
{
return t => t.CustomerId == 1;
}
}
This works as expected.
In most cases however, you would like to pass a parameter to Expression. To achieve this I have to change the property to a static function:
public static Expression<Func<Employee, bool>> CustomerPredicate(int id)
{
return t => t.CustomerId == id;
}
And I can use this like this:
ObjectContext.Employees.Where(EmployeePredicates.CustomerPredicate(id))
This works, but now comes the tricky part. I would like to compile this query... Visual studio doesn't give me any compile errors, but when I run this example the following exception is thrown at runtime:
Internal .NET Framework Data Provider error 1025
Just so we're on the same page here is the full code that gives me the exception:
var _compiledQuery = CompiledQuery.Compile<AdventureWorksEntities, int, IQueryable<Employee>>(
(ctx, id) =>
(ctx.Employee.Where(EmployeePredicates.CustomerPredicate(id))
));
Does anyone have a clue why this exception is being thrown? I've taken this way of working from http://www.albahari.com/nutshell/linqkit.aspx. Any help would be much appreciated.
Thanks Jon,
The problem with the approach you describe, is that chaining predicates together will become very hard. What if I need to combine this predicate with another predicate that filters the employees with a certain name? Then you end up with a lot of selects to pass in the parameters.
(ctx, id, name) =>
(ctx.Employee.Select(emp => new {emp, id})
.Where(EmployeePredicates.CustomerPredicate(id))
.Select(emp => new {emp, name})
.Where(EmployeePredicates.NamePredicate(name))
It gets even worse when you're working on joined tables.
(ctx, id, name) =>
(ctx.Employee
.Join(ctx.Contact, e=> e.ContactId, c => c.Id), (emp, cont) => new Container<Employee, Customer> {Employee = emp, Contact = cont})
.Where(EmployeePredicates.CustomerPredicate(id))
.Where(EmployeePredicates.NamePredicate(name))
.Select(t => new EmployeeDTO {Name = t.cont.Name, Customer = e.emp.Customer})
Because each Where() operates on something of type T and returns something of type T, the WherePredicates in the code above must work on the type Container. This makes it very hard to reuse the Predicates. And reuse was the initial goal of this approach...
The problem is that the Entity Framework is trying to examine the expression tree represented by
(ctx, id) => (ctx.Employee.Where(EmployeePredicates.CustomerPredicate(id))
It can't do that, because it doesn't know what EmployeePredicates.CustomerPredicate does.
As for the best fix... I'm not sure. Basically it's got to know at query compile time what the full query looks like, just with the placeholders for parameters.
I suspect the best solution will involve something like this:
public static Expression<Func<Employee, int, bool>> CustomerPredicate()
{
return (t, id) => t.CustomerId == id;
}
... as that raises the abstraction by one level; it gives you an expression tree which uses id as a ParameterExpression, which is something you'll need in order to build the appropriate expression tree to call CompileQuery. It gets a little hard to think about, unfortunately :(