Getting method name from Expression - c#

Given the following method signature:
string GetActionName<TController, T1, TResult>(Expression<Func<TController, Func<T1, TResult>>> expression)
How can I get the method name from the following implementation:
GetActionName<EventsController, int, IEnumerable<EventDto>>(c => c.GetEventsByIdLocation);
Where GetEventsById has the signature of:
IEnumerable<EventDto> GetEventsByIdLocation(int id)
My current attempt is giving me the exception:
Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'.
var convertExpression = (UnaryExpression)expression.Body;
var memberExpression = (MemberExpression)convertExpression.Operand;
return memberExpression.Member.Name;

The main problem is that in your call, you are not actually calling the method, but returning it. This caused the expression tree to contain a call to a method called CreateDelegate.
Once we have that in our methodCallExpression we need to extract the object, and read its value, which is your method.
Disclaimer: This worked for me with a scenario that I think was equal to yours. If it, however, is the best way to solve the larger problem I do not know. It seems to be fairly slow.
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var constantExpression = (ContantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)constantExpression.Value;
return methodInfo.Name
Another option would be to provide an actual method call to the method, like so:
GetActionName<EventsController, IEnumerable<EventDto>>(
c => c.GetEventsByIdLocation(0));
Which would require you to change the method to:
string GetActionName<TController, TResult>(
Expression<Func<TController, TResult>> expression)
{
return ((MethodCallExpression)expression.Body).Method.Name;
}
This solution will probably perform a lot faster. Allthough I havn't done any benchmarks or anything. Also, this option does not tie you to only being able to get the names of methods that take one int parameter.

Related

Expressions with dynamic return types (dynamic keyword)

My ultimate goal here is to make a generic version of IQueryable<T>.OrderBy() that takes a string parameter and an optional sort direction. Something like these:
return myList.OrderBy("Property1");
return myList.Orderby("Property1", SortOrder.Descending);
(The use case would be a website passing in a field the list would sort by.)
To that end, I've been trying to come up with a way of creating the following expression:
obj => obj.PropertyName
I've found a few places in StackOverflow that could help me, and they've gotten me quite close (this one in particular). Butt it doesn't quite get me all the way there. Specifically I keep getting the dreaded
Expression of type 'System.Int64' cannot be used for return type 'System.Object' for value types. I thought I could get away with return dynamic here, but that seems to have fallen over. I could do a Convert, but that seems like it should be unnecessary.
My code for generating the property selector is as follows:
public static Expression<Func<T, dynamic>> GeneratePropertySelector<T>(string propertyName)
{
var objectType = typeof(T);
var property = objectType.GetProperty(propertyName);
ParameterExpression arg = Expression.Parameter(objectType, "obj");
MemberExpression propertyExpression = Expression.Property(arg, propertyName);
// Here is the line that dies
var selectorExpression = Expression.Lambda<Func<T, dynamic>>(propertyExpression, new ParameterExpression[] { arg });
return selectorExpression;
}
And then usage inside the extension method implementation would be:
var ordered = myList.OrderBy(GeneratePropertySelector("Property1"));
For a string property, this works fine; for an int property, error.
Does anyone know how to get around this? I would have thought that dynamic would have helped here, but I'm at a loss. And given my use case, I'm trying to avoid something like this:
return myList.Orderby<int>("MyIntProperty");

How to set parameter of method using reflection

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.

Get actual return type from a Expression<Func<T, object>> instance

I have a method that accepts a Expression<Func<T, object>> instance. I want to get at the actual data type being returned by a specific expression instance, rather than object.
I can get it to work for direct property references, so if I pass in the expression x => x.IntegerProperty I can get a Type reference for an integer. This approach requires converting it to a MemberExpression.
However, I can't get it to work for arbitrary expressions. For instance, if the expression is x => x.IntegerProperty.ToString() I want to get a Type reference for a string. I can't compile this to a MemberExpression, and if I just .Compile() it and check the return type I get "object".
How can I look at the specific expression instance and derive the actual return type?
Something like this might do the trick. It probably doesn't cover every possibility, but it's a start.
public static Type GetObjectType<T>(Expression<Func<T, object>> expr)
{
if ((expr.Body.NodeType == ExpressionType.Convert) ||
(expr.Body.NodeType == ExpressionType.ConvertChecked))
{
var unary = expr.Body as UnaryExpression;
if (unary != null)
return unary.Operand.Type;
}
return expr.Body.Type;
}
While not impossible, this is particularly difficult. It would require walking the expression tree and doing some potentially complex logic. For example, what would you want to see if I passed in the following expression?
Func<bool, object> expr = switch => switch ? 1 : "False";
This method could either return an int or a string.
Now, you might be able to make more headway by offloading some of this logic on the compiler. You could change your method parameter from Func<T, object> to Func<T, TReturn> and use typeof(TReturn) within the method to determine what the compiler decided the return type of the expression was.
Of course, in the case of my example, you'll still be working against object. But, your example of x => x.IntegerProperty.ToString() will yield string, which is what you're looking for.
Bit of a cheeky way (and it involves actually invoking the Func), but you can do this:
using System;
class Program
{
static Func<T,object> MakeFunc<T>()
{
return x => 23;
}
static Type GetReturnType<T>(Func<T,object> f)
{
return f(default(T)).GetType();
}
static void Main(string[] args)
{
Type t = GetReturnType(MakeFunc<string>());
Console.WriteLine(t);
}
}
It's not guaranteed to work in all situations, I should add - particularly if the default(T) isn't a valid parameter to the Func. But it's a potential starting point at least.

Dynamically get the result of a Func<T,bool> invocation

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);

Accessing calling object from MethodCallExpression

I'm trying to learn about Expression trees, and I've created a method that takes an
Expression<Func<bool>>
and executes it if it satisfies some conditions - see the code below.
private static void TryCommand(Expression<Func<bool>> expression)
{
var methodCallExpression = expression.Body as MethodCallExpression;
if (methodCallExpression == null)
{
throw new ArgumentException("expression must be a MethodCallExpression.");
}
if (methodCallExpression.Object.Type != typeof (MyClass))
{
throw new ArgumentException("expression must be operating on an instanceof MyClass.");
}
var func = expression.Compile();
var success = func.Invoke();
if(!success)
{
Console.WriteLine(methodCallExpression.Method.Name + "() failed with error code " + (func.Target as MyClass).GetError());
}
}
The problem that
(func.Target as MyClass)
is null. Clearly I'm doing something wrong! How do I access the instance that the method is operating on?
Akash, once you have a MethodCallExpression it's simple to recover the method caller.
You must recover the MemberExpression and build an Expression tree that evaluates it.
See the code below:
MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
MemberExpression memberExpression = (MemberExpression)methodCallExpression.Object;
Expression<Func<Object>> getCallerExpression = Expression<Func<Object>>.Lambda<Func<Object>>(memberExpression);
Func<Object> getCaller = getCallerExpression.Compile();
MyClass caller = (MyClass)getCaller();
Hope this helps,
Ricardo Lacerda Castelo Branco
The target of the method call is an instance of MyClass, but the delegate itself isn't the method call. It's something which will perform the method call when it's executed.
If you look at func.Target, you'll see it's a System.Runtime.CompilerServices.ExecutionScope.
Now you could test for that, cast to it, and then fetch either the Locals or the Globals (not sure which) to get the target. However, I suspect it would be cleaner just to change to use a Func<int> (or whatever type your error code is) and return the error code when you execute the delegate in the first place. Then you wouldn't even need an expression tree.
EDIT: Given your comments, I'd suggest:
public static void TryCommand(Expression<Func<MyClass,bool>> command,
MyClass c)
{
// Code as before to find the method name etc.
Func<MyClass, bool> compiled = command.Compile();
if (!compiled(c))
{
Console.WriteLine(methodCallExpression.Method.Name
+ "() failed with error code " + c.GetError());
}
}
You'd then call it with:
TryCommand(x => x.SomeMethod(), myClass);
The target is null because the method is static. In reflection Invoke(..) on a static MethodInfo will ignore the target. This is likely an extension method, in which case the first argument is the inferred target.
Since most of LINQ is based on extension methods you'll see this quite often going forward with reflection.

Categories