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.
Related
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!
I have the following class, for which usage is not important. What is important is method SetCacheItemSelector which takes one parameter, a select expression that projects Account entity to AccountCacheDTO:
public class AccountRepositoryCache : RepositoryCache<Account, AccountCacheDTO>
{
public AccountRepositoryCache()
{
SetCacheItemSelector(x => new AccountCacheDTO
{
Id = x.Id,
Login = x.Login
});
}
}
So signature for this method is:
public void SetCacheItemSelector(Expression<Func<TEntity, TCacheItem>> selector)
In this case, TEntity is Account class, and TCacheItem is AccountCacheDTO class.
Is there a way to use reflection to build select expression dynamically for all the properties that are matching for both Account class and AccountCacheDTO class?
Goal is to have method that would look like this:
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>()
{
... // implementation with reflection goes here
}
EDIT:
Here is final implementation (pretty much the same as the accepted answer):
public static Expression<Func<TSource, TTarget>> BuildSelector<TSource, TTarget>()
{
Type targetType = typeof(TTarget);
Type sourceType = typeof(TSource);
ParameterExpression parameterExpression = Expression.Parameter(sourceType, "source");
List<MemberBinding> bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties().Where(x => x.CanRead))
{
PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);
if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
MemberExpression propertyExpression = Expression.Property(parameterExpression, sourceProperty);
bindings.Add(Expression.Bind(targetProperty, propertyExpression));
}
}
NewExpression newExpression = Expression.New(targetType);
Expression initializer = Expression.MemberInit(newExpression, bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, parameterExpression);
}
I didn't test it, but you should be able to do something like: This is just to convey a general idea and you should be able to tweak it for your requirements.
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>(TEntity entity)
{
List<MemberBinding> memberBindings = new List<MemberBinding>();
MemberInitExpression body = null;
foreach (var entityPropertyInfo in typeof(TEntity).GetProperties())
{
foreach (var cachePropertyInfo in typeof(TCacheItem).GetProperties())
{
if (entityPropertyInfo.PropertyType == cachePropertyInfo.PropertyType && entityPropertyInfo.Name == cachePropertyInfo.Name)
{
var fieldExpressoin = Expression.Field(Expression.Constant(entity), entityPropertyInfo.Name);
memberBindings.Add(Expression.Bind(cachePropertyInfo, fieldExpressoin));
}
}
}
var parameterExpression = Expression.Parameter(typeof(TEntity), "x");
var newExpr = Expression.New(typeof(TCacheItem));
body = Expression.MemberInit(newExpr, memberBindings);
return Expression.Lambda<Func<TEntity, TCacheItem>>(body, parameterExpression);
}
Of course, the #Aravol's answer can make sense, but it is a little different which required in OP. Here is the solution which is more suitable to OP requirement.
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>()
{
Type type = typeof(TEntity);
Type typeDto = typeof(TCacheItem);
var ctor = Expression.New(typeDto);
ParameterExpression parameter = Expression.Parameter(type, "p");
var propertiesDto = typeDto.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var memberAssignments = propertiesDto.Select(p =>
{
PropertyInfo propertyInfo = type.GetProperty(p.Name, BindingFlags.Public | BindingFlags.Instance);
MemberExpression memberExpression = Expression.Property(parameter, propertyInfo);
return Expression.Bind(p, memberExpression);
});
var memberInit = Expression.MemberInit(ctor, memberAssignments);
return Expression.Lambda<Func<TEntity, TCacheItem>>(memberInit, parameter);
}
Your best bet is to get very comfortable with the System.Linq.Expressions namespace, which contains all of the methods you'll need to dynamically metacode your method calls and compile them into delegates. See especially Expression.Call and Lambda.Compile methods. Note that using Lambda.Compile, you can also have a true, compiled Delegate, instead of an expression tree (Expression) wrapping the call to your desired method. (NOTE: You can also forgo the Compile step if you really want that expression tree for later)
As for building your set, that's Assembly scanning, and is going to be a matter of iterating over all classes in your Assembly. I highly recommend you utilize, at the very least, a custom Attribute on your assembly or future assemblies to mark them for this scan, lest this process end up much more costly. At the most, you should consider using a custom Attribute to mark which properties you want scanned for this expression build.
the actual code to this tends to start with
AppDomain.CurrentDomain // Necessary to get all available Assemblies
.GetAssemblies() // Gets all the assemblies currently loaded in memory that this code can work with
.AsParallel() // Highly recommended to make the attribute-checking steps run asynchronously
// Also gives you a handy .ForAll Method at the end
// TODO: .Where Assembly contains Attribute
.SelectMany(assembly => assembly.GetTypes())
// TODO: .Where Type contains Attribute
.SelectMany(type => type.GetProperties)
// TODO: Make sure Property has the right data...
.Select(CompileFromProperty)
Where CompileFromProperty is a method taking PropertyInfo and returning the desired Expression.
Look into ToList() and ToDictionary after that, as you may need to break out of the parallelization once you start pushing values to your cache
Addendum: you also have .MakeGenericType on the Type class, which will allow you to specify Generic parameters from other Type variables, which will prove invaluable when building the Expressions. Don't forget about Contravariance when you define the generic types!
How to convert PropertyInfo to property expression which can be used to invoke StructuralTypeConfiguration<TStructuralType>.Ignore<TProperty>(Expression<Func<TStructuralType, TProperty>> propertyExpression) method?
I tried to use Expression.Property() to construct expression but I am getting following error when I use this expression as propertyExpression parameter:
The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly.
This error probably refers to TProperty type parameter which I don't know how to specify having only PropertyInfo.
I am doing this in relation to: Use Entity Framework's StructuralTypeConfiguration.Ignore() to Ignore all properties but specified set.
UPDATE
Code which is not working:
var propertyInfo = typeof(Foo).GetProperties()[0];
var expression = Expression.Default(typeof(Foo));
var expressionProperty = Expression.Property(expression, propertyInfo);
Ignore(expressionProperty);
var entityType = propertyInfo.DeclaringType;
var parameter = Expression.Parameter(entityType, "entity");
var property = Expression.Property(parameter, propertyInfo);
var funcType = typeof(Func<,>).MakeGenericType(entityType, propertyInfo.PropertyType);
var lambda = Expression.Lambda(funcType, property, parameter);
structureConfiguration.GetType()
.GetMethod("Ignore")
.MakeGenericMethod(propertyInfo.PropertyType)
.Invoke(structureConfiguration, new[]{lambda});
Property expressions require the property access to be on a specific object. There's a few options you can take here. First, if this is being done within one of your entity objects, you can simple use a ConstantExpression to build the property expression:
// Already have PropertyInfo in propInfo
Expression.Property(Expression.Constant(this, this.GetType()), propInfo)
However, since you need a Expression<Func<TStructuralType, TProperty>>, then it seems like you're going to have to build it using a ParameterExpression:
ParameterExpression pe = Parameter.Expression(typeof(MyEntity), "eParam");
Expression propExp = Expression.Property(pe, propInfo);
HOWEVER, here's the kicker... This is just a MemberExpression. To convert to the expression you need, you need to use Expression.Lambda to get a Func<> expression of the type you need. The problem? You don't know the type of the property to define the generic parameters of the lambda expression!
Expression<Func<MyEntity, ????>> eFunc = Expression.Lambda<Func<MyEntity, ????>>(propExp, pe);
This is the crux of the problem of doing it this way. That's not to say it can't be done... It's just that using this method IN THIS WAY isn't going to work. You'll have to use a bit runtime and static typing trickery (as well as judicious use of Actions instead of Funcs) to get this to work correctly.
TProperty exists only in the c# source code text. The compiler always resolves it to a concrete type. If you have a method
void Test<T>(T arg)
{
}
and call it like this
Test("hello");
Test(3);
The compiler generates code for two methods!
void Test(string arg)
{
}
void Test(int arg)
{
}
This means that you have to supply concrete types for your generic parameters if you want to have an invokable method.
This code will get you an Expression<Func<>> of the desired type. Note that there is an Expression.Lambda(...) override that doesn't need you to specify the type of the Func returned.
var t = typeof(Foo);
var pi = t.GetProperty(...);
var prm = Expression.Parameter(t, t.Name);
var prx = Expression.Property(prm, pi);
var lambda = Expression.Lambda(prx, prm);
Note that in many cases you don't have to bother with creating the Expression<Func<>> this way, assuming structureConfiguration below is a StructureConfiguration<Foo>, type inference will allow you to write something like this:
structureConfiguration.Ignore(f => f.Bar);
I have a fairly simple expression parser that uses the Linq.Expression namespace.
Input is something like: (1+1), it finds the left and right constants, and converts the operator char to an ExpressionTypes enum value, creates the appropriate expression, compiles and executes.
I'd really like to be able to do string manipulations, (abc+def) would evaluate to abcdef for instance, however:
System.InvalidOperationException: The binary operator Add is not defined for the types 'System.String' and 'System.String'
How would I go about implementing this myself?
Something like the equavlant to an ExpressionTypes.Concat would be ideal.
You have to create the MethodCallExpression yourself, which in this case is for the static method string.Concat. (This is what the compiler does itself when you compile such code)
Here's some code that demonstrates using MethodInfo to extend the definition of the binary expression Add to concatenation of strings.
One example uses the existing Concat method in the String type.
The second example uses a custom Concat method in the Program type (the class this is all embedded in):
private MethodInfo ConcatMethod = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) });
private MethodInfo ConcatMethod2 = typeof(Program).GetMethod("Concat", new Type[] { typeof(string), typeof(int) });
public static string Concat(string obj1, int obj2)
{
return string.Concat(obj1, obj2.ToString());
}
private Expression SomeCode(string leftStr, string rightStr)
{
Expression left = Expression.Constant(leftStr);
Expression right = Expression.Constant(rightStr);
return Expression.Add(left, right, ConcatMethod);
}
private Expression SomeCode(string leftStr, int rightInt)
{
Expression left = Expression.Constant(leftStr);
Expression right = Expression.Constant(rightInt);
return Expression.Add(left, right, ConcatMethod2);
}
static void CheesyTest2()
{
Program foo = new Program();
Expression exp = foo.SomeCode("abc", "def");
LambdaExpression lambdaExpression = Expression.Lambda(exp);
Delegate func = lambdaExpression.Compile();
object result = func.DynamicInvoke();
Console.WriteLine(string.Format("Expression result: {0}", result));
exp = foo.SomeCode("abc", 42);
lambdaExpression = Expression.Lambda(exp);
func = lambdaExpression.Compile();
result = func.DynamicInvoke();
Console.WriteLine(string.Format("Expression2 result: {0}", result));
}
For custom operators it's actually a method call so you need to find the corresponding method and create the expression tree for calling that method. Haven't work much with expression trees so im affraid I can't give you a code sample but I hope this Helps none the less
I am trying to serialize something based upon meeting particular criteria.
To this end my original hope was to use attributes containing a lambda expression on an object's properties.
However, as this cannot be done I've settled for having a Func<T,bool> member within the class and passing the type (or first parameter type) and name of this Func through the property attribute. E.g.:
Func<SomeObject, bool> func = (p => p.Value == 4);
[FuncAtt(typeof(SomeObject), "func")]
public SomeObject PropertyName { get; set;}
In my serializer I need to call this Func<T, bool>.
Let's assume I have a Type t which is equal to typeof(SomeObject) in this case, or more abstractly, typeof(T). I can also get the Func<T,bool> itself, but only through reflection as an object.
My naive approach is something along these lines:
object func = typeof(MyClass).GetField(attribute.FuncName).GetValue(MyClassInstance);
Type funcType = typeof(Func<,>).MakeGenericType(attribute.Type, typeof(bool));
ParameterExpression p = Expression.Parameter(attribute.Type, objectToSerialize);
LambdaExpression l = Expression.Lambda(funcType, func, p); /* Won't work */
But this leads to the problem of casting a lambda to a delegate which is apparently erroneous.
I tried this in place of 'func':
(Expression)((Action)(() => func))
But that relies on func being a method call not a lambda.
So, can anyone point me in the right direction?
You can just do something like this, without need for expressions:
public static class Test
{
public static Predicate<int> func = s => s > 20;
}
and to get the value:
private void Form1_Load(object sender, EventArgs e)
{
var a = typeof(Test).GetField("func");
bool validates = ((Predicate<int>)a.GetValue(null)).Invoke(100);
}
edit to get the value without knowing the type:
bool validates = (bool)((Delegate)a.GetValue(null)).DynamicInvoke(100);
I think you can use Compile method of a lambda expression to cast it to a delegate.
here's what I found on MSDN:
The Expression<(Of <(TDelegate>)>)
type provides the Compile method,
that compiles the code represented by
the expression tree into an executable
delegate. This executable code is
equivalent to the executable code that
would have been generated had the
lambda expression been assigned to a
delegate type originally.
Here you can find it.
Not sure this is working sample, but this is the way:
// not sure what are you doing in this line, but assume it should return
// a method name specified in the attribute, e.g. "func" in your example.
// Also "func" must be a method (static one in my example) of SomeObject class
String funcname = typeof(MyClass).GetField(attribute.FuncName).GetValue(MyClassInstance);
ParameterExpression param = Expression.Parameter(typeof(SomeObject), "p");
MethodCallExpression call = Expression.Call(SomeObject, funcname, new Type[] { typeof(SomeObject), typeof(Boolean) }, param);
LambdaExpression lambda = Expression.Lambda<Func<SomeObject, Boolean>>(call, param);
now you can call the "func" method like this:
Boolean result = lambda.Compile()(SomeObjectInstance);