I'm constructing an expression tree (Expression<Func<PlainAddress, bool>> predicate) to pass it to the IQueryable.Where function for HNibernate to execute the query. When I pass:
predicate = y => y.Address.City == geoObject.Name;
everything works fine. When I pass:
var x = Expression.Parameter(typeof(PlainAddress));
Expression<Func<PlainAddress, string>> expression = y => y.Address.City;
predicate = Expression.Lambda<Func<PlainAddress, bool>>(
Expression.Equal(
Expression.Invoke(expression, x),
Expression.Constant(geoObject.Name)),
x);
I get the following exception
Invalid path: 'y.Address.City'
[.Where(NHibernate.Linq.NhQueryable`1[BPPDicsManager.Domain.Entities.PlainAddress],
Quote((60ee8287-3f42-426a-8c15-41f62f58623c, ) => (String.op_Equality((y, ) =>
(y.Address.City)60ee8287-3f42-426a-8c15-41f62f58623c, p1))), )]
What am I doing wrong? What are the difference between these 2 options?
I'm not familiar with NHibernate, but I can try to explain the differences between the two expressions.
1.The major difference is the use of Expression.Invoke in sample # 2, which creates an InvocatonExpression that "represents an expression that applies a delegate or lambda expression to a list of argument expressions". This introduces a kind of indirection - loosely speaking,
Sample # 1 is:
y => y.Address.City == geoObject.Name;
whereas Sample # 2 is something like:
x => new Func<PlainAddress, string>(y => y.Address.City).Invoke(x)
== geoObject.Name
You can get the second sample closer to the first by using Expression.Property to create the appropriate MemberExpressions :
var predicate = Expression.Lambda<Func<PlainAddress, bool>>(
Expression.Equal(
Expression.Property(Expression.Property(x, "Address"), "City"),
Expression.Constant(geoObject.Name)),
x);
This is now closer to:
x => x.Address.City == AsStringLiteral(geoObject.Name)
(I'm guessing this will be sufficient to get the predicate to work with NHibernate.)
2.The other difference of course, is the use of Expression.Constantin sample # 2, which will eagerly evaluate the value of geoObject.Name and turn that into a literal in the expression-tree. You're going to need a lot more work if you want to 'emulate' the hoisting of the geoObject variable (or perhaps this?) as in sample # 1.
Related
Newbie LINQ Expressions question-
Expression<Func<TLookupModel, TValue>> idSelector;
IEnumerable<TLookupModel> source;
TValue id;
I'm trying to do (pseudo-code):
source.AsQueryable().FirstOrDefault(x => idSelector == id)
My feeble attempt thus far is along the lines of:
var expressionParam = idSelector.Parameters.First();
ConstantExpression selectedValueConstant = Expression.Constant(id, typeof(TValue));
var idEqualExpression = Expression.Equal(idSelector, selectedValueConstant);
var lambda = Expression.Lambda<Func<TLookupModel, bool>>(idEqualExpression, expressionParam);
var selectedSourceItem = source.AsQueryable().FirstOrDefault(lambda);
I think that gives you a guess as to how I've been thinking so far. I've tried with and without the parameters, different combinations of Expression method calls, trying to get the "parameter" to come from the FirstOrDefault() call, but after reading lots of tutorials I can't get my head around how to extend a "member" expression to equal a constant in this way.
You got really close.
Your idExpression is an Expression in the form of x => x.Property. However, you're passing the whole expression to the Equal expression. Change that to pass only the body:
var idEqualExpression = Expression.Equal(idSelector.Body, selectedValueConstant);
Then you can compile the lambda and pass it to FirstOrDefault without casting to a queryable:
var selectedSourceItem = source.FirstOrDefault(lambda.Compile());
I have the following filter :
Expression<Func<Employee, bool>> fromDateFilterFourDays = z => EntityFunctions.TruncateTime(z.FiringDate) >= EntityFunctions.TruncateTime(DateTime.Now.AddDays(-4));
Expression<Func<Employee, bool>> fromDateFilterSixDays = z => EntityFunctions.TruncateTime(z.FiringDate) >= EntityFunctions.TruncateTime(DateTime.Now.AddDays(-6));
How can I make a delegate out of this filter ?
I don't want to create a variable for each given number , i.e. for four days or six days .
My understanding is that you want to:
Take in two parameters to the delegate, the employee and the number of days.
Compile that expression into a delegate.
The first part can be done by adding the days to the parameter list:
Expression<Func<Employee, int, bool>> fromDateFilter = (z, n) => EntityFunctions.TruncateTime(z.FiringDate) >= EntityFunctions.TruncateTime(DateTime.Now.AddDays(n));
The second by using the Compile method:
var del = fromDateFilter.Compile();
// use it
del(employee, -4);
You can easily turn Expression<Func<...>> to Func<...> by using Compile method.
However, keep in mind that the sample expressions you provided will not work, because they are using Canonical Functions which are just placeholders for mapping the corresponding database SQL functions, and will throw exception if you try to actually evaluate them (which will happen with Func).
From the other side, if the question is actually how to parametrize the sample expressions, it could be like this
static Expression<Func<Employee, bool>> DateFilter(int currentDateOffset)
{
return e => EntityFunctions.TruncateTime(e.FiringDate) >= DateTime.Today.AddDays(currentDateOffset);
}
This is done by calling the Invoke() method:
fromDateFilterFourDays.Invoke(employee);
Or you can Compile() the expression to a func and then call the func:
var fromDateFilterFourDaysFunc = fromDateFilterFourDays.Compile();
fromDateFilterFourDaysFunc(employee);
I've been following this tutorial today to learn a bit about the Web API and noticed something - in the tutorial code, there was this line:
var product = products.FirstOrDefault((p) => p.Id == id);
As you can see the parameter "p" is provided in parentheses. Since it's not mandatory having it this way, I'm curious if there's any benefit to doing it that way or if it's just a preference of the developer?
In this case, it's purely developer preference.
Parenthesises are needed when you have more than one parameter. For example:
var singleString = someStrings.Aggregate((current, next) => current + Environment.NewLine + next);
The C# specification explicitly states that (p) => ... can be written as p => ...:
7.15 Anonymous function expressions
...
In an anonymous function with a single, implicitly typed parameter, the parentheses may be omitted from the parameter list. In other words, an anonymous function of the form
( param ) => expr
can be abbreviated to
param => expr
So there is no technical difference whatsoever, only personal preference.
I am trying to combine a multiple selection with a lambda function into an lambda expression. How do I do that? I know the last line is wrong, but giving you an idea of what I mean.
Func<Event, bool> where = null;
if (!string.IsNullOrWhiteSpace(searchToken))
where = q => q.Name.ToUpper().Contains(searchToken.ToUpper());
where += q => q.Hidden = false;
Expression<Func<Event, bool>> where1 = q => where; <-- Erroring
I suspect you want PredicateBuilder. (The source is available on that page.) You'd use it like this:
var predicate = q => !q.Hidden;
if (!string.IsNullOrWhiteSpace(searchToken))
{
predicate = predicate.And(q => q.Name.ToUpper()
.Contains(searchToken.ToUpper());
}
return predicate;
That's assuming you want to "and" the conditions - you never made that clear...
Note that that is not a good way to compare in a case-insensitive way, either. If you could tell us what's going to consume the query (e.g. LINQ to SQL, LINQ to EF) we could suggest a provider-compatible way of performing a case-insensitive query.
Look at http://msdn.microsoft.com/en-us/library/bb882637.aspx. How to use expression trees to build dynamic queries.
AFAIK when using Expression <> like that the expression must be known in compile time, because the compiler then build AST abstract syntax three and stores it as data in your Expression <> instance.
Is there a way to make the ProjectID check below part of an optional block? I'm a recent .Net convert from Java EE and I'm looking for something similar to the Hibernate Criteria API. I'd like to simplify the block below and only have to call Where() once. I'm also not sure of the performance implications of doing a Where() with lambdas as I just started working with .Net a week ago.
public IQueryable<Project> FindByIdOrDescription(string search)
{
int projectID;
bool isID = int.TryParse(search, out projectID);
IQueryable<Project> projects;
if (isID)
{
projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search) || p.ProjectID == projectID);
}
else
{
projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search));
}
return projects;
}
If you're looking for optionally adding AND xyz, you could simply chain Where calls:
var projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search));
if (isID)
projects = projects.Where(p => p.ProjectID == projectID);
Unfortunately things are not so easy when you'd like to do an OR xyz. For this to work, you'll need to build a predicate expression by hand. It's not pretty. One way to do this is
Expression<Func<Project, bool>> predicate = p => p.ProjectDescription.Contains(search);
if (isID)
{
ParameterExpression param = expr.Body.Parameters[0];
predicate = Expression.Lambda<Func<Project, bool>>(
Expression.Or(
expr.Body,
Expression.Equal(
Expression.Property(param, "ProjectID"),
Expression.Constant(projectID))),
param);
}
var projects = dataContext.Projects.Where(predicate);
Note that adding a condition to the existing predicate is a lot more work than creating the initial expression, because we need to completely rebuild the expression. (A predicate must always use a single parameter; declaring two expressions using lambda syntax will create two separate parameter expression objects, one for each predicate.) Note that the C# compiler does roughly the same behind the scenes when you use lambda syntax for the initial predicate.
Note that this might look familiar if you're used to the criteria API for Hibernate, just a little more verbose.
Note, however, that some LINQ implementations are pretty smart, so the following may also work:
var projects = dataContext.Projects.Where(
p => p.ProjectDescription.Contains(search)
|| (isID && p.ProjectID == projectID));
YMMV, though, so do check the generated SQL.
The query isn't parsed and executed until the first time you actually access the elements of the IQueryable. Therefore, no matter how many where's you keep appending (which you're not even doing here anyway) you don't need to worry about hitting the DB too many times.
Delegates / expressions can be "chained", like this (untested pseudo-code):
Expression<Predicate<Project>> predicate = p => p.ProjectDescription.Contains(search);
if ( isID )
predicate = p => predicate(p) || p.ProjectID == projectId;
return dataContext.Where(predicate);