Multiple LINQ expressions, and dynamic properties - c#

1.) I have an entity query like: return myEntityStore.Query<theType>(x => x.Name == "whatevs");
However, ideally I want to call a function which appends other expressions to the original query, like OrderBy's and more Where's.
Something like this:
public IQueryable<T> ProcessQuery<T>(System.Linq.Expressions.Expression<Func<T, bool>> theOriginalQuery) where T: class
{
return EntityStore.Query<T>(theOriginalQuery).Where(x => x.Active == true).OrderBy(x => x.OrderBy);
}
2.) One of the obvious problems here is that I'm working with <T>, so is there a way to specify the property as a string or something?
<T>(expression).OrderBy(x => "x.ThisColumnExistsIPromise");
3.) The Query function already transforms the expression into a Where(), so would it be sufficient to simply do a Where(expression).Where(expression)?
So, at the end of the day, is the below possible to achieve?
entityStore.Query<T>(originalExpression).Where(additionalExpression).Where(x => "x.Active == true").OrderBy(x => "x.OrderBy");

Actually the question isn't so clear. But I noticed that your problem might be solved if you used LINQ Dynamic Query Library then you could use Property name as string to do whatever you want (OrderBy, Where ....)
For more information look at this

Related

Combining expression trees

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.

Passing Parameters into an Expression Specification using LinqToSQL

I want to reduce duplicate logic in a LinqToSQL query by using Expression<Func<T,bool>>. We've successfully done the before using static properties like so:
public static Expression<Func<Document, bool>> IsActive
{
get
{
return document => !document.Deleted;
}
}
...
_workspace.GetDataSource<Document>().Where(DocumentSpecifications.IsActive)
However I am struggling to get this working when additional parameters need to be passed into the Expression like so:
public static Expression<Func<Comment, bool>> IsUnread(int userId, Viewed viewed)
{
return
c =>
!c.Deleted && c.CreatedByActorID != actorId
&& (viewed == null || c.ItemCreatedDate > viewed.LastViewedDate);
}
...
// Throwing "Argument type 'System.Linq.Expression<X,bool>' is not assignable
// to parameter type 'System.Func<X,bool>'"
return (from a in alerts
select
new UnreadComments
{
TotalNumberOfUnreadComments =
a.Comments.Count(CommentSpecifications.IsUnread(actorId, a.LastView))
})
How do I convert the specification so it can be accepted in this way and would it still convert to SQL correctly?
EDIT: Following Anders advice I added .Compile() to the query. It now works correctly when unit testing in memory collections; however when LinqToSQL trys to convert it into SQL I get the following exception:
System.NotSupportedException: Unsupported overload used for query operator 'Count'
I've tried:
a.Comments.Count(CommentSpecifications.IsUnread(actorId, a.LastView).Compile())
a.Comments.AsQueryable().Count(CommentSpecifications.IsUnread(actorId, a.LastView))
It looks like the second query is executed as linq-to-objects and not as linq-to-sql. It expects a Func<X, bool> which is what linq-to-objects use, while linq-to-sql (or any other IQueryable provider) expects an uncompiled expression tree that can be translated to something else)
A quick fix is to call Compile() on the expression to convert it to an executable function.
a.Comments.Count(CommentSpecifications.IsUnread(actorId, a.LastView).Compile())
To be more detailed you really should figure out why that query is executed as linq-to-objects and not linq-to-sql. Especially if you expected it to be translated to efficient sql it could become a performance nightmare.
Update
After your edit it's more obvious what's happening:
You're running the query as linq-to-objects during unit testing and as linq-to-sql later. In that case converting the expression to a Func<> through Compile() won't work as linq-to-sql won't recognize it.
Update 2
Composing reusable part into query expression that are to be translated is hard - it confuses the translation engine. Linq-to-sql is somewhat more tolerant than linq-to-entities is, but it is nevertheless hard to get it work. A better way is often to make chaining functions that operate on IQueryable<T>.
public static IQueryable<Comment> WhereIsUnread(this IQueryable<Comment> src, int userId)
{
return src.Where(
c =>
!c.Deleted && c.CreatedByActorID != actorId
&& (viewed == null || c.ItemCreatedDate > c.Alert.LastView.LastViewedDate));
}
...
return (from a in alerts
select
new UnreadComments
{
TotalNumberOfUnreadComments =
a.Comments.WhereIsUnRead(actorId).Count()
})
Something like that should work. Notice I've rewritten how the last viewed date is accessed, as it would otherwise fail translation to SQL when passed in as a parameter.

Inline delegate for Linq to Entities

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;
}

How to get unique string from a lambda expression

I would like to cache a EF4 result using a generic repository & memcached. This all works great, except for the fact that I need a unique key representation for a lambda expression.
For example a call to the generic repository could be:
Repository<User> userRepository = new Repository<User>();
string name = "Erik";
List<User> users_named_erik = userRepository.Find(x => x.Name == name).ToList();
In my generic repository function I need to make a unique key out of the lambda expression to store it in memcached.
So I need a function that can do this
string GetPredicate(Expression<Func<T,bool>> where)
{
}
The result of the function should be this string x=> x.Name == "Erik". And not something like +<>c__DisplayClass0) which I get from the ToString() method.
Thanks in advance.
Take a look at this blog entry.
Basically, you're looking to do a "Partial Evaluation" of the expression to account for the value introduced by the closure. This MSDN Walkthrough (under the heading "Adding the Expression Evaluator") has the code that is referenced by the blog entry for incorporating the unevaluated closure members in the resulting string.
Now, it may not work with complex expressions, but it looks like it will get you on your way. Just keep in mind that it doesn't normalize expressions. That is x => x.Name == "Erik" is functionally equivalent to x => "Erik" == x.Name (and all other variants).

Compiled LinQ query with with expressions in functions

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 :(

Categories

Resources