I have expression helper class that I use for server side filtering for the column names that I check dynamically such as:
public static Expression<Func<TItem, bool>> PropertyContains<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("Contains", new Type[] { typeof(string) });
var body = Expression.Call(m, mi, c);
return Expression.Lambda<Func<TItem, bool>>(body, param);
}
This one works perfectly fine. I also have another static helper method to compare if the values are equal:
public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
PropertyInfo property, TValue value)
{
var param = Expression.Parameter(typeof(TItem));
var xx = Expression.Property(param, property);
var body = Expression.Equal(Expression.Property(param, property),
Expression.Constant(value));
return Expression.Lambda<Func<TItem, bool>>(body, param);
}
That as well works perfectly fine.
BUT, for the decimal fields (Decimal(19,4) in SQL) the issue I am having is, user only see the 2 digits as decimal place on UI lets say $19.32
So when they enter 19.32 into search field, it does not match with db record as the record in db is actually 19.3224 let say. So mathematically 19.32 != 19.3224 but as I only show 2 decimal places on UI, it does not return the record user looking for.
My problem is, I am not sure how to manipulate db record's value in Expression. Or even using Decimal.Truncate would be sufficient for now (which means it is ok if I ignore the decimal places and only compare the int part)
I tried something like below:
public static Expression<Func<TItem, bool>> PropertyEqualMoneyFields<TItem>(PropertyInfo propertyInfo, decimal value)
{
var param = Expression.Parameter(typeof(TItem));
var m = Expression.MakeMemberAccess(param, propertyInfo);
var c = Expression.Constant(value, typeof(decimal));
var mi = typeof(decimal).GetMethod("Truncate", new Type[] { typeof(decimal) });
var body = Expression.Call(m, mi, c);
return Expression.Lambda<Func<TItem, bool>>(body, param);
}
and the way I call this as:
string filteredColumnNameInDb = "SomeMoneyColumnName"; // coming from ui actually
PropertyInfo filteredProperty = typeof(SomeDto).GetProperty(filteredColumnNameInDb);
query = query.Where(ExpressionHelper.PropertyEqualMoneyFields<SomeDto>filteredProperty,decimal.Truncate(decimalValue)));
but it throws an exception as Static method requires null instance, non-static method requires non-null instance., and even though there would be no exception, that would not work anyways as I still need to apply Expression.Equal at some point to compare if they are equal after the truncate.
If I am totally off the road with this approach, I would love to hear any other way to do this.
Or if this is not clear, that would be very useful if someone can point to "running a sql function on db record before doing any comparison" so maybe I would create some sql function to apply to that record first, then do the Expression.Equal comparison.
PS: I know I can do this easily after ToList() but that is the whole point of the server side filtering as I do NOT want to load the whole dataset!
Related
Given a simple class with arbitrary properties (for discussion lets say Id, Name, and Description)
and given an instance of that class, I want to find matching entries in the database by specifying the property to match
I'm trying to do something in this respect similar to the AddOrUpdate method of EF, but I need the entity returned to me for further processing.
var match = new SomeClass{Name="Whatever"};
var found = Context.SomeClass.Find(x=>x.Name, match);
public static T Find<T>(this DbSet<T> set, Expression<Func<T, object>> matchOn, T matchAgainst) where T : class {
var func = matchOn.Compile();
var valueToFind = func(matchAgainst);
var combinedExpression = //matchon + "=" + valueToFind;
var found = set.FirstOrDefault(combinedExpression);
return found;
}
That gives me the value of the property in the passed in object, but I need to now combine that value with the passed in expression and pass it to the db set.
IE, the code I'm effectively trying to run is set.FirstOrDefault(x=>x.Name==valueToFind) How do I take the matchon expression (which contains x=>x.Name) and combine that with the ==valueToFind to get the x=>x.Name==valueToFind from them?
How do I build the combined expression? (I realize the "string" code above is completely wrong, but I was trying to get across what I need the function to do, but I have no idea what that syntax would look like.)
For manually coded examples, it would be easy enough just to pass in a hardcoded lambda with the value set, but my use case involves running through a collection of objects and finding the match for each one, so the value will not be known until runtime, and the method must work against arbitrary types and various properties, so I can't hardcode the property name either.
If you have a property selector, and a value to compare to, you can get an expression tree like this:
public static Func<TEntity, bool> GetComparer<TEntity,TProperty>(
Expression<Func<TEntity,TProperty>> selector, TProperty value)
{
var propertyRef = selector.Body;
var parameter = selector.Parameters[0];
var constantRef = Expression.Constant(value);
var comparer
= Expression.Lambda<Func<TEntity, bool>>
(Expression.Equal(propertyRef, constantRef), parameter)
.Compile();
return comparer;
}
Sample usage:
var comparer = GetComparer<Person, string>(p => p.Name, "John");
var persons = Person.GetPersons();
var john = persons.FirstOrDefault(comparer);
I have a variable called sortColumn, which contains the text of a column that I want to sort a query result by. I also have a generic repository which takes as a parameter an Expression that contains the field I want to sort by. I can't seem to get from the string property name to an Expression.
So the generic repository that I have contains the following method
public IEnumerable<TEntity> Get<TOrderBy>(Expression<Func<TEntity, bool>> criteria,
Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex,
int pageSize,
bool isAssendingOrder = true,
EnumDeletePolicy deletePolicy = EnumDeletePolicy.ExcludeDeleted)
Notice the second parameter to this Get is Expression-Func-TEntity, TOrderBy. As I mentioned I have a variable called sortColumn, which contains the string for a property on my TEntity object I need to convert this string into an Expression that I can pass to the Get method.
Here is what I have right now.
var parameter = Expression.Parameter(typeof(IContract));
var memberExpression = Expression.Property(parameter, data.SortColumn);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
Which creates an object of type LambdaExpression. The actual type of this LambdaExpression is an Expression-Func-IContract, string (or whatever the type sortColumn of the property is). If I call the Get method and pass in this LambdaExpression and explicitly cast it to the Expression type then it will work fine. The problem is I don't know what the Expression type is, it could be a string, int, int?, etc. It all depends on the type of the property that is specific in the sortColumn property.
Can you help me make this last jump to the right Expression type?
Edit based on Marc's suggestions:
I nearly have this working, actually based specifically on the question it is working, but I have 1 remaining problem.
The IContract which is the Entity Type that I'm querying against actually inherits from IRelationship. If I specify a field from the IContract interface then the code above works. If I specify a field from the IRelationship interface then the following line fails.
var memberExpression = Expression.Property(parameter, data.SortColumn);
If I try something like below so that I'm grabbing the MemberExpression from the IRelationship, but building the Lambda based on IContract I get an error from the repository.
var parameter = Expression.Parameter(typeof(IRelationship));
var memberExpression = Expression.Property(parameter, data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, Expression.Parameter(typeof(IContract)));
The error that I get is "The parameter '' was not bound in the specified LINQ to Entities query expression."
The final expression to get it working was this
var parameter = Expression.Parameter(typeof(IContract));
var memberExpression = Expression.Property(parameter, typeof(IRelationship), data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, parameter);
So I needed to specify the middle parameter to the memberExpression line, to say look in the inherited Relationship interface for the property
You kinda need to use the correct generic overload - which used to mean you had to use MakeGenericMethod; however, you can also use dynamic to avoid the need to use MakeGenericMethod here, for example (in this case via Where, but the important point is how it works):
IQueryable<Foo> source = new[] { new Foo { Bar = 123 } }.AsQueryable();
Expression<Func<Foo,bool>> typed = x=>x.Bar == 123;
LambdaExpression untyped = typed;
IQueryable<Foo> filtered = Queryable.Where(source, (dynamic)untyped);
Note: you can't use extension methods here - hence why you need to use Queryable.*.
For an OrderBy example using your code:
var parameter = Expression.Parameter(typeof(Foo));
var memberExpression = Expression.Property(parameter, "Bar");
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
LambdaExpression untyped = lambdaExpression;
IQueryable<Foo> sorted = Queryable.OrderBy(source, (dynamic)untyped);
var all = sorted.ToArray();
Re the edit:
var parameter = Expression.Parameter(typeof(IRelationship));
var memberExpression = Expression.Property(
Expression.Convert(parameter, typeof(IContract)), data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, parameter);
The following code is a helper I've created for a system which allows administrators to create their own queries on a database. It returns a lambda expression based on the method and value provided.
I am attempting to find a way to pass parameters to the method call - in this example I am using the StartsWith parameter of String, and attempting to set StringComparison.OrdinalIgnoreCase as a parameter.
There will be others parameters required too, depending on the type of the property specified. I'm hoping that understanding the method of supplying the string comparison property will enable me to add the rest later.
The underlying code works correctly, I just need it to be case-insensitive.
I have used this question as a guide, but the solution does not seem applicable here.
Here is the code:
public static class LambdaExpressionHelper<T> {
public static Expression<Func<T, bool>> Build(string propertyName, string method, string propertyValue) {
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
ParameterExpression e = Expression.Parameter(typeof(T), "e");
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
ConstantExpression c = Expression.Constant(propertyValue, m.Type);
MethodInfo mi = m.Type.GetMethod(method, new Type[] { m.Type }, );
// The below caused errors
//object classInstance = Activator.CreateInstance(typeof(T), null);
//object[] paramArray = new object[] { StringComparison.OrdinalIgnoreCase };
//mi.Invoke(classInstance, paramArray);
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
}
// External code:
var lambda = LambdaExpressionHelper<MailingListMember>.Build("EmailAddress", "StartsWith", "RoRy#");
Thanks for any help.
In the general case this should work:
public static Expression<Func<T, bool>>
Build(string propertyName, string method, params object[] args)
{
var propertyInfo = typeof(T).GetProperty(propertyName);
var e = Expression.Parameter(typeof(T), "e");
var m = Expression.MakeMemberAccess(e, propertyInfo);
var mi = m.Type.GetMethod(method, args.Select(a => a.GetType()).ToArray());
var c = args.Select(a => Expression.Constant(a, a.GetType())).ToArray();
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
The idea is that you accept any number of constant arguments, and use their types to select the appropriate overload when calling GetMethod. After that, you create the appropriate number of constant expressions from those arguments to pass to Expression.Call.
So with the above code, you could do:
var l1 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#");
var l2 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#", StringComparison.OrdinalIgnoreCase);
If you also need to get the value StringComparison.OrdinalIgnoreCase from the string "StringComparison.OrdinalIgnoreCase", I would factor this out into a separate method so that the interface of Build can remain generic. Exactly how to do it is covered in Getting Enum value via reflection (and I guess in lots of similar questions as well).
Note: I don't have convenient access to a compiler right now, so please excuse any mistakes.
I'm not sure i understand your question exactly, but hopefully this will help you on the way.
If you are using .NET 4.0 you can use the new keyword "dynamic" for much easier reflection code:
dynamic myDynamicObj = "Hello World!"; // or Activator.CreateInstance...
var doesIndeed = myDynamicObj.StartsWith("Hello", StringComparison.OrdinalIgnoreCase);
This does evaluate at run time so make sure spelling/case etc is correct.
Edit:
Assuming you always wanted to call methods with one String-arg returning bool, a possible solution would be to create a delegate type -
delegate bool CompareString(String str);
and than have the second argument of Build as that type:
Build(String .., CompareString cs, String ...)
But this does not work if you need to add extra arguments, as in the second arg of type StringComparison. A if/switch could be the answer there though, i.e if(CompareString is StartsWith)...
Sorry, not at a windows-computer so i can't test further.
I am trying to dynamically build an expression tree in C#, which is compiled and used as the predicate for LINQ-to-SQL Where() call. The problem is that I am trying to compare an Enum (with int as its underlying type) directly against an Int, but this is failing with the error "The member MyEnumType has no supported translation to SQL".
Code:
ParameterExpression param = Expression.Parameter(typeof(MyClass), "obj"); //input parameter - this exposes a property of the Enum type
MemberExpression enumMember = Expression.Property(param, "MyEnumProperty"); //used to get the Enum typed property from the parameter
//MISSING STEP TO CAST THE ENUM OF THE MEMBER EXPRESSION TO AN INT?
BinaryExpression binaryExpr = Expression.Equal(enumMember, Expression.Constant(1));
LambdaExpression<Func<MyClass, bool>> whereClause = Expression.Lambda(binaryExpr, param);
//when whereClause is used to filter LINQ-to-SQL results, the error is thrown
I'm fairly new to expression trees and I can't figure this out. I have tried using
Expression.Convert(enumMember, typeof(int))
as the first part of the BinaryExpression but this doesn't fix it.
Any help is much appreciated.
Simply, you shouldn't have to, as long as you've told LINQ-to-SQL about the enum (rather than mapping it as an int and having a separate property in C# that does the translation). For example, the following works fine:
var param = Expression.Parameter(typeof(DomainObject));
var body = Expression.Equal(Expression.Property(param, "SomeProperty"),
Expression.Constant(YourEnumType.SomeEnum));
var predicate = Expression.Lambda<Func<DomainObject, bool>>(body, param);
var count = db.Table.Where(predicate).Count();
The main point is that my SomeProperty property is mapped in the dbml to the enum. Simply overtype the type name with the enum type (including namespace).
Likewise, you shouldn't be giving it a 1, but rather the typed enum; for example:
Expression.Constant(Enum.ToObject(typeof(YourEnumType), 1))
(if all you know is 1)
Thanks To Marc Gravell. (Expression Guru !) See Correct answer.
I made a change to an Expression Routine to cater for this scenario.
Normal properties or Enums. Incase someone finds this helpful
public static Expression<Func<TPoco, bool>> GetEqualsPredicate<TPoco>(string propertyName,
object value)
Type fieldType )
{
var parameterExp = Expression.Parameter(typeof(TPoco), #"t"); //(tpoco t)
var propertyExp = Expression.Property(parameterExp, propertyName);// (tpoco t) => t.Propertyname
var someValue = fieldType.IsEnum // get and eXpressionConstant. Careful Enums must be reduced
? Expression.Constant(Enum.ToObject(fieldType, value)) // Marc Gravell fix
: Expression.Constant(value, fieldType);
var equalsExp = Expression.Equal(propertyExp, someValue); // yes this could 1 unreadble state if embedding someValue determination
return Expression.Lambda<Func<TPoco, bool>>(equalsExp, parameterExp);
}
look my friend first of all you have to modify your enum to be like that:
public enum myenum : int
{
item1 = 0,
item2 = 1
}
after that you can convert between int and that eunm by that way:
int x = (int) myenum.item1;
just you must convert enum to object
you can use this :
Expression.Constant(Enum.ToObject(enumMember.Type, enumMember .value))
Try
(int) enumMember
From my recent question, I try to centralize the domain model by including some silly logic in domain interface. However, I found some problem that need to include or exclude some properties from validating.
Basically, I can use expression tree like the following code. Nevertheless, I do not like it because I need to define local variable ("u") each time when I create lambda expression. Do you have any source code that is shorter than me? Moreover, I need some method to quickly access selected properties.
public void IncludeProperties<T>(params Expression<Func<IUser,object>>[] selectedProperties)
{
// some logic to store parameter
}
IncludeProperties<IUser>
(
u => u.ID,
u => u.LogOnName,
u => u.HashedPassword
);
Thanks,
Lambdas are great for many scenarios - but if you don't want them, perhaps simply don't use them? I hate to say it, but simple strings are tried and tested, especially for scenarios like data binding. If you want fast access, you could look at HyperDescriptor, or there are ways of compiling a delegate to the property accessors, or you can build an Expression from the string and compile it (including a cast to object if you want a known signature, rather than calling the (much slower) DynamicInvoke).
Of course, in most cases even crude reflection is fast enough, and isn't the bottleneck.
I suggest starting with the simplest code, and check it is actually too slow before worrying about it being fast. If it isn't too slow, don't change it. Any of the above options would work otherwise.
Another thought; if you are using Expression, you could do something like:
public void IncludeProperties<T>(
Expression<Func<T,object>> selectedProperties)
{
// some logic to store parameter
}
IncludeProperties<IUser>( u => new { u.ID, u.LogOnName, u.HashedPassword });
and then take the expression apart? A bit tidier, at least... here's some sample code showing the deconstruction:
public static void IncludeProperties<T>(
Expression<Func<T, object>> selectedProperties)
{
NewExpression ne = selectedProperties.Body as NewExpression;
if (ne == null) throw new InvalidOperationException(
"Object constructor expected");
foreach (Expression arg in ne.Arguments)
{
MemberExpression me = arg as MemberExpression;
if (me == null || me.Expression != selectedProperties.Parameters[0])
throw new InvalidOperationException(
"Object constructor argument should be a direct member");
Console.WriteLine("Accessing: " + me.Member.Name);
}
}
static void Main()
{
IncludeProperties<IUser>(u => new { u.ID, u.LogOnName, u.HashedPassword });
}
Once you know the MemberInfos (me.Member in the above), building your own lambdas for individual access should be trivial. For example (including a cast to object to get a single signature):
var param = Expression.Parameter(typeof(T), "x");
var memberAccess = Expression.MakeMemberAccess(param, me.Member);
var body = Expression.Convert(memberAccess, typeof(object));
var lambda = Expression.Lambda<Func<T, object>>(body, param);
var func = lambda.Compile();
Here's the shortest expression I can come up with:
public static void IncludeProperties(Expression<Action<IUser>> selectedProperties)
{
// some logic to store parameter
}
public static void S(params object[] props)
{
// dummy method to get to the params syntax
}
[Test]
public void ParamsTest()
{
IncludeProperties(u => S(
u.Id,
u.Name
));
}