BuildPredicate comparing two fields - c#

I am trying to map comparisons between two fields, where previously I had only been doing field to Expression.Constant comparisons.
private static Expression<Func<TDomainModel, bool>> BuildPredicate<TDomainModel>(string leftPropName, string rightPropName, TypeMap map)
{
PropertyMap leftPropMap = map.GetPropertyMaps().FirstOrDefault(pro => pro.DestinationProperty.Name == leftPropName);
Expression leftParam = leftPropMap.CustomExpression.Body;
PropertyMap rightPropMap = map.GetPropertyMaps().FirstOrDefault(pro => pro.DestinationProperty.Name == rightPropName);
Expression rightParam = rightPropMap.CustomExpression.Body;
Expression operatorBody = Expression.GreaterThanOrEqual(leftParam, rightParam);
return Expression.Lambda<Func<TDomainModel, bool>>(operatorBody, leftPropMap.CustomExpression.Parameters[0]);
}
However I always get an exception...
The parameter 's' was not bound in the specified LINQ to Entities query expression.
Both properties are on the same entity, and even use 's' in their mappings. No idea how to fix this. I've tried putting both CustomExpression.Parameters values into the returned Lambda, but it complains of too many overloads then.
Thanks for any help.

You need to rewrite the right expression using the same parameter as the left expression. They're different now.
I used to do this creating a Expression.Invoke node to the right side, then expanding invocations with a ExpressionVisitor.

Related

How to create an expression in C# from string?

There are methods in MongoDB C# driver (or any other libraries) that expect an expression only as a parameter like:
query.SortByDescending(x => x.Name) // sort by name for example
What I would like to do is to receive the field "Name" as a string and be able to sort by it at runtime:
var sortBy = "Name";
query.SortByDescending(x => x."SortyBy") // something like this
i.e how to create an expression to be x => x.(value of sortBy)?
This question basically has the same answer as your question yesterday.
The reason the answer is essentially the same is simply that FieldDefinition<T> is implicitly castable from string, so you can pass a string anywhere you see a FieldDefinition<T> argument.
Therefore, we can use the .Sort method (since that takes a SortDefinition<T>) and use Builders<T>.Sort.Descending which takes a FieldDefinition<TElement> and returns the SortDefinition<T> that we need:
query = query.Sort(Builders<Items>.Sort.Descending(sortBy));
I'm assuming your document type is Items here as it was in your question yesterday.
Note that the textual name you use here has to match the name in the database, which isn't necessarily the same as your document's property name. If you want to make it more robust, then you'll want to use expression trees to produce an expression for the property in question.
An alternative solution would be to build the Expression<Func<T, TElement>> that SortByDescending requires:
private static Expression<Func<TDocument, object>> GetPropertyExpression<TDocument>(string propertyName)
{
var parameter = Expression.Parameter(typeof(TDocument));
var property = Expression.Property(parameter, propertyName);
var castResult = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<TDocument, object>>(castResult, parameter);
}
query = query.SortByDescending(GetPropertyExpression<Items>(sortBy));
As far as Mongo is concerned, this is no different to having written query = query.SortByDescending(i => i.Name);

How do I "capture" a string variable in an expression tree, instead of having a constant? [duplicate]

Hi, I'm trying to build an Expression to get a generic entity by its primary key and getting a parameterized sql query.
Currently I can get the correct WHERE query, but it isn't parameterized.
public async Task<TDbo> Get(TKey key, Expression<Func<TEntity, TKey>> keySelector)
{
var propertyRef = keySelector.Body;
var parameter = keySelector.Parameters[0];
var constantRef = Expression.Constant(key);
var equals = Expression.Equal(propertyRef, constantRef);
var comparer = Expression.Lambda<Func<TEntity, bool>>(equals, parameter);
return await _context.Set<TDbo>().SingleOrDefaultAsync(comparer);
}
This results in the following query:
SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = 1\r\nLIMIT 2,
instead of the wanted:
SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = #__s_0\r\nLIMIT 2
It's because of Expression.Constant(key). Value constant expressions are not parameterized by the query translator. What you need is an expression referring to a property or field of another expression (which could be constant). That's basically what C# compiler emits for closures.
One way is to actually use the C# compiler to create lambda expression with closure and take the body:
Expression<Func<TKey>> keyValue = () => key;
var variableRef = key.Body;
(the variableRef is a replacement of yours constantRef)
Another way is to use anonymous, tuple or specific class type to create explicit closure instance and bind the corresponding property or field. For instance, with anonymous type:
var variableRef = Expression.Property(Expression.Constant(new { key }), "key");
or with System.Tuple:
var variableRef = Expression.Property(Expression.Constant(Tuple.Create(key)), "Item1");
The actual method doesn't really matter (I personally prefer the first variant with lambda) - all they will cause creating parameter by EF Core query translator.

nhibernate queryover LIKE with expression trees

I'm looking to add a method to my base repository class that allows me to use LIKE expressions but I'm not quite sure of how to go about this. I want to create a generic method that looks at the expression tree passed in and looks for wildcard characters in the string values passed in. It would then generate the QueryOver statement accordingly.
I have the following currently:
public IList<T> FindAll(Expression<Func<T, bool>> criteria, char wildCard)
{
return SessionFactory.GetCurrentSession()
.QueryOver<T>()
.Where(criteria)
.List();
}
Obviously the hard part is yet to come. I need to look through the expression tree and build the query using QueryOver dynamically. Looking for some pointers on how to proceed with this. Or am I just wasting my time here and should just create individual methods in my repositories that handle the LIKE queries?
Additional Criteria
Ideally I'd like to tell the difference between the following:
search*
*search
*search*
So the query generated would be:
field LIKE 'search%'
field LIKE '%search'
field LIKE '%search%'
There's two ways to write a Like expression in QueryOver.
If you do it off the Where clause:
.Where(Restrictions.Like(Projections.Property<T>(*projected property*), *string value*, MatchMode.Anywhere))
However this is kinda long to write.
So you can use WhereRestrictionOn:
.WhereRestrictionOn(*projected property*).IsLike(*string value*, MatchMode.Anywhere)
This means you need to pass in two parameters like:
FindAll<User>(x => x.FirstName, "bob");
You may be able to use .Contains, .StartsWith, .EndsWith, but I'm not sure.
FindAll<User>(x => x.FirstName.Contains("bob"));
FindAll<User>(x => x.FirstName.StartsWith("bob"));
FindAll<User>(x => x.FirstName.EndsWith("bob"));
I don't think those work in NHibernate.
Hope that helps.
I don't really understand what you want to do. Do you want for a query such as
session.QueryOver<T>().Where(x => x.property == "*substring*").List();
to generate a property LIKE "%substring%" query? In most Linq providers the String.Contains method is transformed in a "LIKE" query, and thus you wouldn't need to look for wildcard characters in the expression tree, only for the String.Contains method.
In case of the latter, you would have to parse the expression tree looking for a String.Contains() method. This could be very troublesome (http://msdn.microsoft.com/en-us/library/bb397951.aspx). Also, I can't see in your method which property is to be "compared" with the LIKE operator.
Anyways, I think it would be easier to pass a ICriterion to your .Where(), such as
.Where(new NHibernate.Criterion.LikeExpression("property", "%value%"))
, and append your other conditions with .And() right after that. The drawback is losing strongly-typed queries.
After digging for a while for a solution about the problem of translating expressions of the form
session.QueryOver<T>().Where(x => x.StringAttrbute.StartsWith("ajoofa"))
into SQL of the form
SELECT * FROM {table} WHERE {string_attribute} LIKE 'ajoofa%'
I came up with the following solution: Yu have to Register custom method calls for the Standard string functions .Contains(), .StartsWith, .EndsWith().
God knows why these functions are not registered by default within NHibernate. The following code should help you out.
/// Perform the registration of custom methods
/// </summary>
public static void Register()
{
if (!_registered)
{
_registered = true;
String str = null;
ExpressionProcessor.RegisterCustomMethodCall(() => str.StartsWith(null), ProcessStartsWith);
ExpressionProcessor.RegisterCustomMethodCall(() => str.EndsWith(null), ProcessEndsWith);
ExpressionProcessor.RegisterCustomMethodCall(() => str.Contains(null), ProcessContains);
}
}
static ICriterion ProcessStartsWith(MethodCallExpression methodCallExpression)
{
ExpressionProcessor.ProjectionInfo projection = ExpressionProcessor.FindMemberProjection(methodCallExpression.Object);
object value = ExpressionProcessor.FindValue(methodCallExpression.Arguments[0]) + "%";
return projection.CreateCriterion(Restrictions.Like, Restrictions.Like, value);
}
static ICriterion ProcessEndsWith(MethodCallExpression methodCallExpression)
{
ExpressionProcessor.ProjectionInfo projection = ExpressionProcessor.FindMemberProjection(methodCallExpression.Object);
object value = "%" + ExpressionProcessor.FindValue(methodCallExpression.Arguments[0]);
return projection.CreateCriterion(Restrictions.Like, Restrictions.Like, value);
}
static ICriterion ProcessContains(MethodCallExpression methodCallExpression)
{
ExpressionProcessor.ProjectionInfo projection = ExpressionProcessor.FindMemberProjection(methodCallExpression.Object);
object value = "%" + ExpressionProcessor.FindValue(methodCallExpression.Arguments[0]) + "%";
return projection.CreateCriterion(Restrictions.Like, Restrictions.Like, value);
}

"Or" Together Expression<Func<EntityFramework Entity, bool>> in .NET 4, Silverlight RIA domain service

I have a small custom object defined as:
public class TimeSeriesDefinition
{
public int classID;
public DateTime startTime;
public DateTime endTime;
}
I'm passing a List classIDs, a List startTimes, and a List endTimes into an RIA Domain Service function. As a matter of organization, I was grouping these values into a List of TimeSeriesDefinitions and then trying to use a foreach loop to create an expression that would select with AND operators between the values in a class and OR operators between each class or implement a ".Any" query as suggested by the first answer I received below. The problem is that I can't use the TimeSeriesDefinition class in a DomainService function because it is not a primitive type or one of my entity types (maybe I should just make an entity with this type?), so I need another method of achieving the desired query results. My original idea for using expressions that I never got anywhere with is here:
Expression<Func<EventLog, bool>> bounds;
Boolean assignedBounds = false;
foreach (TimeSeriesDefinition ts in reporters)
{
if (assignedBounds.Equals(false))
{
bounds = c => c.reporterID == ts.classID && c.reportDateTime >= ts.startTime && c.reportDateTime <= ts.endTime;
assignedBounds = true;
}
else
{
Expression<Func<EventLog, bool>> newBounds = c => c.reporterID == ts.classID && c.reportDateTime >= ts.startTime && c.reportDateTime <= ts.endTime;
bounds = Expression.Or(Expression.Invoke(bounds), Expression.Invoke(newBounds);
// bounds = Expression<Func<EventLog, bool>>.Or(bounds, newBounds);
}
}
return this.ObjectContext.EventLog.Where(bounds);
My goal is for the resultset to have all records of a ts.classID between ts.startDate and ts.EndDate. From what I've found online, it seems that making sure the parameters are correctly assigned is tricky as well, but right now I'm still getting a
"Cannot implicitly convert type 'System.Linq.Expressions.BinaryExpression' to 'System.Linq.Expressions.Expression>'"
error at the line
bounds = Expression.Or(Expression.Invoke(bounds), Expression.Invoke(newBounds);
Can anybody point me in the right direction? I suppose I could possibly build this whole thing into a query string somehow, but I'd rather not go there.
Thanks in advance for your insight!
If you Funcletize (localize) your references to TimeSeriesDefinition on the client side, you should be able to include them in your query (see Evaluator.PartialEval method in here http://msdn.microsoft.com/en-us/library/bb546158.aspx). You should be able to simply call it on your Expression object and have the references to TimeSeriesDefinitions lifted away to primitive constants:
Evaluator.PartialEval(lambdaExpression);
As for your compilation problem:
bounds = Expression.Or(Expression.Invoke(bounds), Expression.Invoke(newBounds);
The left hand side of that assignment is a generic LambdaExpression. The right hande side is a BinaryExpression. To do the assignment, you need to Lambda the Or and also provide a ParameterExpression for your InvocationExpressions:
var parameterExpression = Expression.Parameter(typeof(EventLog));
bounds = Expression.Lambda<Func<EventLog, bool>>(
Expression.Or(
Expression.Invoke(bounds, parameterExpression),
Expression.Invoke(newBounds, parameterExpression),
parameterExpression);
However...you will probably run into the wonderful fact that RIA doesn't support InvocationExpressions... (I haven't verified this but I know EF doesn't). You've got to Expand your InvocationExpressions to inline them (sort of like with the Funcletlizer mentioned above).
LINQKit ( http://www.albahari.com/nutshell/linqkit.aspx ) provides one out of the box. It also provides helper methods for combining criteria as you mention above. If you don't want the whole dependency on LINQKit, you can grab the source for the same thing here: http://www.java2s.com/Open-Source/CSharp/Content-Management-Systems-CMS/Kooboo/Microsoft/Data/Extensions/DataExtensions.cs.htm
Then just change your Where to:
return this.ObjectContext.EventLog.Where(InvocationExpander.Expand(bounds));
Instead of List<TimeSeriesDefinition> can you use List<Tuple<int, DateTime, DateTime>>. Your query would then be this...
return ObjectContext.EventLog.Where(c =>
reporters.Any(r =>
c.reporterID == r.Item1 &&
c.reportDateTime >= r.Item2 &&
c.reportDateTime <= r.Item3));

when and in what scenario to use Expression Tree

I was reading about Expression Tree feature and how you can create delegates using lambda expressions. I still can't get as to in what scenario it is useful and in what real world example should I use it.
The primary use for expression trees is for out-of-process LINQ providers such as LINQ to SQL.
When you write something like this:
var query = people.Where(x => x.Age > 18)
.Select(x => x.Name);
those lambda expressions can either be converted to delegates, which can then be executed (as they are in LINQ to Object) or they can be converted to expression trees, which can be analyzed by the query source and acted on accordingly (e.g. by turning them into SQL, web service calls etc). The difference is that expression trees represent the code as data. They can be compiled into delegates if necessary, but usually (within LINQ anyway) they're never executed directly - just examined to find out the logic they contain.
Expression trees are also used extensively in the Dynamic Language Runtime, where they represent the code which should execute when a dynamic expression is evaluated. Expression trees are well suited for this as they can be composed and broken down again, and after they're compiled the resulting IL is JIT-compiled as normal.
Most developers will never need to mess with the expression tree API, although it has a few other uses.
Aside from LINQ, another very simple use case is to extract both the name and the value of a property. I use this in a fluent API for validating data transfer objects. It's safer to pass one lambda parameter to define both name and value rather than have a second string parameter for the name, and run the risk of developers getting it wrong.
Here's an example (minus all the safety checks and other housekeeping):
public Validator<T> Check<T>(Expression<Func<T>> expr) {
// Analyse the expression as data
string name = ((MemberExpression) expr.Body).Member.Name;
// Compile and execute it to get the value
T value = (expr.Compile())();
return new Validator<T>(name, value);
}
Example of use:
Check(() => x.Name).NotNull.MinLength(1);
Check(() => x.Age).GreaterThan(18);
I used expression trees to make a null-safe evaluator:
string name = myObject.NullSafeEval(x => x.Foo.GetBar(42).Baz.Name, "Default");
This methods analyzes and rewrites the expression tree to insert null checks before each property or method call along the "path" to Name. If a null is encountered along the way, the default value is returned.
See implementation here
Expression trees are also commonly used to avoid referring to a property by hard-coding its name in a string:
private string _foo;
public string Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged(() => Foo);
// Rather than:
// OnPropertyChanged("Foo");
}
}
static string GetPropertyName<T>(Expression<Func<T>> expr)
{
var memberExpr = expr.Body as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("expr", "The expression body must be a member expression");
return memberExpr.Member.Name;
}
protected void OnPropertyChanged<T>(Expression<Func<T>> expr)
{
OnPropertyChanged(GetPropertyName(expr));
}
This enables compile time checking and name refactoring

Categories

Resources