I have 3 classes.
AutoYearMake{
int Year { get; set; }
string Make { get; set; }
}
AutoModel{
IAutoYearMake AutoYearMakeParent { get; set; }
string Model { get; set; }
}
AutoTrim{
IAutoModel AutoModelParent { get; set; }
string Trim { get; set; }
}
I need to create a query to a database. How can I get dynamically an expression like:
Expression<Func<AutoTrim, bool>> expression = expression = t => t.AutoModelParent.AutoYearMakeParent.Year == year.Value
&& t.AutoModelParent.AutoYearMakeParent.Make
== make && t.AutoModelParent.Model == model;
This is my code. It doesn't work.
ParameterExpression parameter = Expression.Parameter(typeof (AutoTrim), "a");
MemberExpression yearProp = Expression.Property(parameter, "AutoModelParent.AutoYearMakeParent.Year");
MemberExpression makeProp= Expression.Property(parameter, "AutoModelParent.AutoYearMakeParent.Make");
MemberExpression modelProp= Expression.Property(parameter, "AutoModelParent.Model");
Expression right = Expression.Constant(2014);
Expression e1 = Expression.Equal(yearProp, right);
right = Expression.Constant("make");
Expression e2 = Expression.Equal(makeProp, right);
right = Expression.Constant("model");
Expression e3 = Expression.Equal(modelProp, right);
Expression predicateBody = Expression.AndAlso(e1, e2);
Expression final = Expression.AndAlso(e1, e2);
How can I resolve this problem? I tried to use Expression.Call. It wasn't right way.
The first argument of the Expression.Property() methods is object from which property should be accessed. If you want to access property AutoModelParent from parameter t you use:
Expression.Property(parameter, "AutoModelParent")
So, if you want to access Model property of AutoModelParent property of parameter t you can go with:
Expression.Property(Expression.Property(parameter, "AutoModelParent"), "Model")
Consider this solution:
private static MemberExpression GetPropertyPathAccessor(Expression parameter, string path)
{
return (MemberExpression) path.Split('.').Aggregate(parameter, Expression.Property);
}
or if you don't like one-liners
private static MemberExpression GetPropertyPathAccessor(Expression parameter, string path)
{
Expression current = parameter;
foreach (var propertyName in path.Split('.'))
{
current = Expression.Property(current, propertyName);
}
return (MemberExpression)current;
}
Then you can use:
MemberExpression yearProp = GetPropertyPathAccessor(parameter, "AutoModelParent.AutoYearMakeParent.Year");
How about this?
After that you have the Func<> in expFunc and you can use it right away.
var argParam = Expression.Parameter(typeof(AutoTrim), "s");
var expFunc = Expression.Lambda<Func<AutoTrim, bool>>(
Expression.AndAlso(
Expression.AndAlso(
Expression.Equal(
Expression.Property(Expression.Property(Expression.Property(argParam, "AutoModelParent"), "AutoYearMakeParent"), "Year"),
Expression.Constant(year.Value)),
Expression.Equal(
Expression.Property(Expression.Property(Expression.Property(argParam, "AutoModelParent"), "AutoYearMakeParent"), "Make"),
Expression.Constant(make))
),
Expression.Equal(
Expression.Property(Expression.Property(argParam, "AutoModelParent"), "Model"),
Expression.Constant(model))
),
argParam
).Compile();
But of course you need these too (just example values):
int? year = 2000;
string make = "BMW";
string model = "6";
Related
I have searched, and found similar posts pertaining to my issue, however nothing seems to solve my problem.
I am fairly new to C#, and this is my first attempt at building an expression tree. (please go easy ;-)
I am trying to create an expression tree which would, once compiled, filter values on a set of data.
Here is my expression method:
private static Expression<Func<TItem, bool>> CreateFilterExpression<TItem>(string propertyName, string expressionType, dynamic filterValue)
{
if (param == null)
{
param = Expression.Parameter(typeof(TItem), "item");
}
MemberExpression member = GetMemberExpression<TItem>(propertyName);
//When we call our method, we need to evaluate on the same type
//we convert the filter value to the type of the property we are evaluating on
dynamic convertedValue = Convert.ChangeType(filterValue, member.Type);
MethodInfo method = member.Type.GetMethod(expressionType, new[] { member.Type });
ConstantExpression constantValue = Expression.Constant(convertedValue, member.Type);
Expression containsMethodExp;
if (expressionType == "NotEqual")
{
method = member.Type.GetMethod("Equals", new[] { member.Type });
}
if (member.Type.ToString().ToLower() == "system.string")
{
//We need to compare the lower case of property and value
MethodCallExpression propertyValueToLowerCase = Expression.Call(member, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
MethodCallExpression filterValueToLowerCase = Expression.Call(constantValue, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
containsMethodExp = Expression.Call(propertyValueToLowerCase, method, filterValueToLowerCase);
}
else if (member.Type.ToString().ToLower() == "system.datetime")
{
//we need to compare only the dates
MemberExpression dateOnlyProperty = Expression.Property(member, "Date");
containsMethodExp = Expression.Call(dateOnlyProperty, method, constantValue);
}
else
{
containsMethodExp = Expression.Call(member, method, constantValue);
}
if (expressionType == "NotEqual")
{
containsMethodExp = Expression.Not(containsMethodExp);
}
return Expression.Lambda<Func<TItem, bool>>(containsMethodExp, param);
}
private static MemberExpression GetMemberExpression<TItem>(string propertyName)
{
if (param == null)
{
param = Expression.Parameter(typeof(TItem), "item");
}
MemberExpression member = null;
//Check if we have a nested property
if (propertyName.Contains('.'))
{
Expression nestedProperty = param;
string[] properies = propertyName.Split('.');
int zeroIndex = properies.Count() - 1;
for (int i = 0; i <= zeroIndex; i++)
{
if (i < zeroIndex)
{
nestedProperty = Expression.PropertyOrField(nestedProperty, properies[i]);
}
else
{
member = Expression.Property(nestedProperty, properies[i]);
}
}
}
else
{
member = Expression.Property(param, propertyName);
}
return member;
}
Example usage would be like so:
var lambda = CreateFilterExpression<T>("Some.Nested.Object", "Equals", "Some value");
var compiled = lambda.Compile();
gridData = gridData.Where(compiled);
An example of the data I trying to ultimately bind to my grid looks like this:
public class Some : BaseClass
{
public decimal NumberAvailable { get; set; }
public DateTime EffectiveDate { get; set; }
public Batch Batch { get; set; }
public decimal Price { get; set; }
public decimal Limit { get; set; }
public NestedClass Nested { get; set; }
public int? CompanyId { get; set; }
public decimal Amount { get; set; }
}
public class NestedClass : BaseClass
{
public int RequestId { get; set; }
public string Code { get; set; }
public string Company { get; set; }
public string Reference { get; set; }
}
The problem occurs when we have null value on an object, like "Some.Nested = null", and then trying to convert "Reference" to lowercase. Here:
MethodCallExpression propertyValueToLowerCase = Expression.Call(member, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
Here is the result in debugger:
How can I check for null values, on nested objects, and return empty string if it is null?
I hope I explained my question well enough. Thank you in advance!
What you want to do is to generate an expression like this:
Some == null ? null : Some.Nested == null ? null : Some.Nested.Object
This unfortunately is no longer a member expression, so GetMemberExpression wouldn’t work for this. Instead you need a chain of conditional expression that accesses one more level at a time.
Once you have that, you could then do <memberExpression> ?? string.Empty to get a string which you can safely operate on.
To generate the latter expression, you can use Expression.Coalesce:
Expression.Coalesce(memberExpression, Expression.Constant(string.Empty))
For the member expression itself, you could write something like this:
Expression AccessMember(Expression obj, string propertyName)
{
string[] parts = propertyName.Split(new char[] { '.' }, 2);
Expression member = Expression.PropertyOrField(obj, parts[0]);
if (parts.Length > 1)
member = AccessMember(member, parts[1]);
return Expression.Condition(Expression.Equal(obj, Expression.Constant(null)),
Expression.Constant(null, member.Type), member);
}
This can be used like this:
string path = "Some.Nested.Object";
string[] parts = path.Split(new char[] { '.' }, 2);
ParameterExpression param = Expression.Parameter(typeof(T), parts[0]);
Expression memberAccess = AccessMember(param, parts[1]);
memberAccess would then be exactly the above chained conditional expression.
Combined into your function (simplified only for strings for now), it could look like this:
Expression<Func<TObj, bool>> BuildFilterExpression<TObj, TMember>(string propertyPath, TMember comparisonValue, TMember defaultValue)
{
string[] parts = propertyPath.Split(new char[] { '.' }, 2);
ParameterExpression param = Expression.Parameter(typeof(TObj), parts[0]);
// get member access expression
Expression memberExpression = AccessMember(param, parts[1]);
// coalesce the member with the default value
memberExpression = Expression.Coalesce(memberExpression, Expression.Constant(defaultValue));
// get the comparison value as expression
Expression comparisonExpression = Expression.Constant(comparisonValue);
// type specific logic
if (memberExpression.Type == typeof(string))
{
MethodInfo toLowerMethod = typeof(string).GetMethod("ToLower", Type.EmptyTypes);
memberExpression = Expression.Call(memberExpression, toLowerMethod);
comparisonExpression = Expression.Call(comparisonExpression, toLowerMethod);
}
// create the comparison expression
Expression filterExpression = Expression.Equal(memberExpression, comparisonExpression);
return Expression.Lambda<Func<TObj, bool>>(filterExpression, param);
}
Used like this:
BuildFilterExpression<SomeType, string>("Some.Nested.Object", "foo bar", string.Empty)
… it essentially creates the following lambda expression:
(Some) => ((Some == null ? null : Some.Nested == null ? null : Some.Nested.Object) ?? string.Empty).ToLower() == "foo bar"
Above code assumes that for a property expression Some.Nested.Object, Some is the object that is being passed to the lambda, so the first property that would be accessed is Nested. The reason is that I simply didn’t know your example object structure, so I had to come up with something.
If you want Some be the first property that is accessed for the passed object, you can easily change that though. To do that, modify the beginning of BuildFilterExpression so that the propertyPath is not split up. Pass some random name (or no name even) to Expression.Parameter, and pass the full propertyPath to AccessMember:
// don’t split up the propertyPath
// let’s call the parameter `obj`
ParameterExpression param = Expression.Parameter(typeof(TObj), "obj");
// get member access expression—for the full property path
Expression memberExpression = AccessMember(param, propertyPath);
Below expression compares property NAME with the value PETER.
ParameterExpression pe = Expression.Parameter(typeof(T), "x");
MemberExpression member = Expression.Property(pe, "name");
ConstantExpression value = Expression.Constant("Peter");
exp = Expression.Equal(member, value);
What if the property is a class:
public class Address
{
public string Name {get; set;}
}
Then the expression would look something similar to this:
MemberExpression member = Expression.Property(pe, "Address.Name");
ConstantExpression value = Expression.Constant("Peter");
exp = Expression.Equal(member, value);
This would fail because the member type doesn't match the value type.
So, the question is: How to build an Expression that would work using the above class sample ??
I use this expression in a NHibernate.Linq query:
var q = from f in data //of type IQueryable<T>
select f;
if (filter != null) //filter of type Expression<Func<T, bool>>
q = q.Where(filter);
etc....
Thank you.
UPDATE by Peter:
Based on the code from xanatos (next post) I have created the following test to understand how it works. Its not very different from what xanatos do, but at first I could not get it to work, so I decided to write it allover in one simple test, and that did it. With thanks to xanatos:
[Test]
public void FilterWithDeepProperties()
{
//Arrange
IGenericGridRepository repository = ObjectFactory.GetInstance<IGenericGridRepository>();
FilterDescriptor filter = new FilterDescriptor("AgreementId.Name", FilterOperator.IsEqualTo, "a name");
string[] properties = filter.Member.Split('.');
ParameterExpression pe = Expression.Parameter(typeof(SampleDomain), "x");
//Act
Expression lastMember = pe;
for (int i = 0; i < properties.Length; i++)
{
MemberExpression member = Expression.Property(lastMember, properties[i]);
lastMember = member;
}
ConstantExpression valueExpression = Expression.Constant(filter.Value);
Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
Expression<Func<SampleDomain, bool>> where = Expression.Lambda<Func<SampleDomain, bool>>(equalityExpression, pe);
var result = repository.GetObjects<SampleDomain>(filter: where);
//Assert
result.Count().Should().BeGreaterThan(0, "because there are many schedule items equals to " + filter.Value);
}
You probably want something like:
public static Expression<Func<TSource, bool>> GetEquality<TSource>(object value, params string[] properties)
{
ParameterExpression pe = Expression.Parameter(typeof(TSource), "source");
Expression lastMember = pe;
for (int i = 0; i < properties.Length; i++)
{
MemberExpression member = Expression.Property(lastMember, properties[i]);
lastMember = member;
}
Expression valueExpression = Expression.Constant(value);
Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(equalityExpression, pe);
return lambda;
}
Use it like:
Expression exp = GetEquality<Person>("Foo", "Address", "Name");
Where Foo is your Peter (so the value that must be compared), while Address and Name are the names of the "chain" of properties. For example I'm using
public class Person
{
public Address Address { get; set; }
}
public class Address
{
public string Name { get; set; }
}
So the expression generated is
source.Address.Name == "Foo"
If you want to use something like Address.Name, you can use the method like
Expression exp = GetEquality<Person>("Foo", "Address.Name".Split('.'));
I am dealing with the scanario of Nullable types during formation of dynamic query expressions. These expressions would fetch filtered data from any SQL Tables( interfacing with Code First classes using EF ).
I have normal object ( e.g Consignment operating on several properties along with Nullable properties).
My expression formation goes well untill I encounter some Nullable types. On these nullables, I am getting
The binary operator NotEqual is not defined for the types 'System.Nullable`1[System.Single]' and 'System.Single'.
For removing this exception, I am using all appraoches regarding convertion posted on different threads.
Invoking lambda expressions in Expression trees
Trying to filter on a Nullable type using Expression Trees
These all are generating expressions with added word "Convert" ( i.e Convert(someValue) ) and in result I always have expression
t=>(t.Consignment.Id = 45000 && t.Consignment.someProperty>=45 Or t.Consignment.Weight! = Convert(5000)).
Of course I need the whole above expression WITHOUT "Convert". Because this "Convert" will not fetch the data from tables accordingly.
Any help would be greatly appreciated! What should left to do? I already know conversion, but this makes the whole expression useless, because it won't project the records because of needless "Convert"
Added
Expression NotEqual<T>(Expression PropertyType, ConstantExpression a_Constant, ParameterExpression parameter)
{
if(IsNullableType(Property.Type) &&!IsNullableType(a_Constant.Type))
{
var converted = a_Constant.Type != Property.Type ? (Expression)Expression.Convert(a_Constant, Property.Type): (Expression)a_Constant;
// here above statement returns (Convert(50000)) and all I want (50000), but i tried all combinitions from Expression in order to form this constant as expression, it always throws exception what I mentioned originally.
var body = Expression.MakeBinary(ExpressionType.NotEqual, PropertyType, converted);
//MakeBinary statement returns {(t.Weight != Convert(5000000))} but I need {(t.Weight != 5000000)}
var expr = Expression.Lambda<Func<T, bool>>(body, parameter);
return expr;
}
}
Code:
public class Consignment
{
public float? Weight { get; set; }
}
public static class GenericQueryExpressionBuilder
{
private static Expression NotEqual<T>(Expression memberExpression, ConstantExpression a_Constant, ParameterExpression parameter)
{
ConstantExpression constantExpression = null;
if (IsNullableType(memberExpression.Type) && !IsNullableType(a_Constant.Type))
{
//var converted = a_Constant.Type != memberExpression.Type ? (Expression)Expression.Convert(a_Constant, memberExpression.Type) : (Expression)a_Constant;
Expression constantExp = Expression.Property(a_Constant,typeof(T),"Weight");
**// above statement throws exception I commented.**
var body = Expression.MakeBinary(ExpressionType.NotEqual, memberExpression, converted);
//here I want "t=>(t.Weight!=5000.0) INSTEAD of t=>(t.Weight!=Convert(5000.0))"
var expr = Expression.Lambda<Func<T, bool>>(body, parameter);
return expr;
}
else if (!IsNullableType(memberExpression.Type) && IsNullableType(a_Constant.Type))
memberExpression = Expression.Convert(memberExpression, a_Constant.Type);
return Expression.NotEqual(memberExpression, constantExpression);
}
static bool IsNullableType(Type t)
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
private static Expression GetExpression<T>(ParameterExpression param, string a_strPropertyName, string Operator, object Value)
{
MemberExpression member = Expression.Property(param, a_strPropertyName);
ConstantExpression constant = Expression.Constant(Value);
try
{
return GenericQueryExpressionBuilder.NotEqual<T>(member, constant, param);
}
catch (InvalidOperationException)
{
return null;
}
return null;
}
public static Expression<Func<T, bool>> GetExpression<T>(Consignment consignment)
{
Expression expression = null;
var parameter = Expression.Parameter(typeof(T), "t");
string PropertyName = "Weight";
string Operation = "NotEqual";
object Value = consignment.Weight;
expression = GenericQueryExpressionBuilder.GetExpression<T>(parameter, PropertyName, Operation, Value);
return Expression.Lambda<Func<T, bool>>(expression, parameter);
}
}
class Program
{
static void Main(string[] args)
{
Consignment consignment = new Consignment();
consignment.Weight = 50000.0f;
var deleg = GenericQueryExpressionBuilder.GetExpression<Consignment>(consignment).Compile();
}
}
Here's a short but complete example showing how to build the c => c.Weight.HasValue && c.Weight.Value != 5000f expression tree. I've removed a lot of irrelevant code from the question:
using System;
using System.Linq.Expressions;
public class Consignment
{
public float? Weight { get; set; }
}
public class Test
{
private static Expression NotEqual(Expression memberExpression,
ConstantExpression constantToCompare)
{
// Other cases removed, for simplicity. This answer only demonstrates
// how to handle c => c.Weight != 5000f.
var hasValueExpression = Expression.Property(memberExpression, "HasValue");
var valueExpression = Expression.Property(memberExpression, "Value");
var notEqual = Expression.NotEqual(valueExpression, constantToCompare);
return Expression.AndAlso(hasValueExpression, notEqual);
}
static void Main(string[] args)
{
Consignment consignment = new Consignment();
consignment.Weight = 50000.0f;
var parameter = Expression.Parameter(typeof(Consignment), "c");
var weight = Expression.Property(parameter, "Weight");
var constant = Expression.Constant(5000f, typeof(float));
var weightNotEqualExpression = NotEqual(weight, constant);
var lambda = Expression.Lambda<Func<Consignment, bool>>
(weightNotEqualExpression, parameter);
Console.WriteLine(lambda);
}
}
My issue is I need to query on the value of a property in a generic class. The property is tagged with an attribute.
See the following code:
var rowKeyProperty = EFUtil.GetClassPropertyForRowKey<T>();
var tenantKeyProperty = EFUtil.GetClassPropertyForTenantKey<T>();
var queryResult =
objContext.CreateObjectSet<T>().Single(l => (((int) tenantKeyProperty.GetValue(l, null)) == tenantKey) &&
(((int)rowKeyProperty.GetValue(l, null)) == KeyValue));
The rowKeyProperty and tenantKeyProperty are of type System.Reflection.PropertyInfo.
I understand why I am getting the error. When the linq query is translated to SQL, it can't understand the property.GetValue.
However, I'm completely stumped as to a work around here. Does anyone have any ideas how to achieve this? Thx.
You need to actually build up the Expression objects to represent the expression that you want this to mimic, in this case the expression you want to represent is:
l => l.SomeProperty == SomeValue
So you need to build up each component of that bit by bit, from creating the parameter, defining the equality operator, the property access, the constant value, etc.
public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
PropertyInfo property, TValue value)
{
var param = Expression.Parameter(typeof(TItem));
var body = Expression.Equal(Expression.Property(param, property),
Expression.Constant(value));
return Expression.Lambda<Func<TItem, bool>>(body, param);
}
Once you have all of that you can call it using the data that you have:
var queryResult = objContext.CreateObjectSet<T>()
.Where(PropertyEquals<T, int>(tenantKeyProperty, tenantKey))
.Where(PropertyEquals<T, int>(rowKeyProperty, KeyValue))
.Single();
Appendix here... Following #Servy answer and based on this topic with a nice answer by #TomBrothers, you can use the same logic to make a StartsWith (or similar) function:
public static Expression<Func<TItem, bool>> PropertyStartsWith<TItem>(PropertyInfo propertyInfo, string value)
{
var param = Expression.Parameter(typeof(TItem));
var m = Expression.MakeMemberAccess(param, propertyInfo);
var c = Expression.Constant(value, typeof(string));
var mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
var body = Expression.Call(m, mi, c);
return Expression.Lambda<Func<TItem, bool>>(body, param);
}
In this case, it forces value to be a string.
It is more correct to specify the type in Expression.Constant(value, typeof(TValue)))
public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
string property, TValue value)
{
var xParameter = Expression.Parameter(typeof(TItem));
var body = Expression.Equal(Expression.Property(xParameter, property), Expression.Constant(value, typeof(TValue)));
return Expression.Lambda<Func<TItem, bool>>(body, xParameter);
}
Or, like this, to check the property. ChangeType
public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
string property, TValue value)
{
var xParameter = Expression.Parameter(typeof(TItem));
var type = typeof(TItem).GetProperty(property).PropertyType;
value = ChangeType<TValue>(value);
BinaryExpression body = Expression.Equal(Expression.Property(xParameter, property), Expression.Constant(value, type));
return Expression.Lambda<Func<TItem, bool>>(body, xParameter);
}
What is it for. I check all class references to classes, I look for "..ID" entries. Somewhere I have a type "int" and "int?".
public class BudgetLimit : BaseRecord
{
[Required]
public int DepartmentID { get; set; }
public virtual Department Department { get; set;}
public int? ProjectID { get; set; }
public virtual Project Project { get; set; }
}
You add .AsEnableable after the LINQ statement.
e.g objectdata.AsEnumerable()
enter link description here
Note: I know it's much simple to create this using dynamic linq but I want to learn.
I want to create a lambda that "finds": Name=David AND Age=10.
class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
var lambda = LabmdaExpression<Person>("Name", "David", "Age", 10);
static Expression<Func<T, bool>> LabmdaExpression<T>(string property1, string value1, string property2, int value2)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(Person), "o");
MemberExpression memberExpression1 = Expression.PropertyOrField(parameterExpression, property1);
MemberExpression memberExpression2 = Expression.PropertyOrField(parameterExpression, property2);
ConstantExpression valueExpression1 = Expression.Constant(value1, typeof(string));
ConstantExpression valueExpression2 = Expression.Constant(value2, typeof(int));
BinaryExpression binaryExpression1 = Expression.Equal(memberExpression1, valueExpression1);
BinaryExpression binaryExpression2 = Expression.Equal(memberExpression2, valueExpression2);
var ret1 = Expression.Lambda<Func<T, bool>>(binaryExpression1, parameterExpression);
var ret2 = Expression.Lambda<Func<T, bool>>(binaryExpression2, parameterExpression);
}
Expression andExpression = Expression.AndAlso(binaryExpression1, binaryExpression2);
return Expression.Lambda<Func<T, bool>>(andExpression , parameterExpression);
Edit - comment
You just chain together all your expresions
so in order to get this expression
X AND (Y OR (Z OR Q))
Expression ZorQ = Expression.OrElse(zExp, qExp);
Expression YorZorQ = Expression.OrElse(yExp, ZorQ);
Expression XandYorZorQ = Expression.AndAlso(xExp, YorZorQ);