Chained methods reflection execution - c#

I need to execute some chained methods using reflection.
What I´m trying to get is an IQueryable of a Entity Core DBContext Set of an unknown compile type:
var type = typeof(x);
this.DBContext.Set(type).AsQueryable();
Obviously, that code does not compile because Set is a generic Method where the type must be defined in compile time:
this.DBContext.Set<TUnknownType>().AsQueryable();
To solve that, I tried to execute the methods using reflection:
MethodInfo dbSetMethod = typeof(DbContext).GetMethod(nameof(DbContext.Set));
MethodInfo generic = dbSetMethod.MakeGenericMethod(property.DeclaringType);
var asQueryableMethod = generic.ReturnType.GetMethod("AsQueryable");
var result = asQueryableMethod.Invoke(this.DbContext, null);
But when I debug the code, I get a null in the line:
var asQueryableMethod = generic.ReturnType.GetMethod("AsQueryable");
Apparently, the dbContext.Set<TUnknownType>() does not have the AsQueryable method. That method is an extension method coming from Linq I guess.
What am I missing? Why is the method unavailable?

That's right, DbSet<T> implements IQueryable<T>, which exposes AsQueryable<T>() extension method.
C# offers two ways to invoke an extension method - as an instance method, i.e.
this.DBContext.Set<UnknownType>().AsQueryable();
or as a static method, i.e.
Queryable.AsQueryable(this.DBContext.Set<UnknownType>());
Reflection APIs, on the other hand, support only the second approach, i.e. the static method way of obtaining and invoking the method. You should get MethodInfo for generic Queryable.AsQueryable, and pass it an appropriate generic type parameter to make a MethodInfo object suitable for invocation.

Related

Getting MethodInfo at compile time for a non-static method

I'm working on a program that calculates expressions at runtime based on a set of inputs, then executes those remotely. This requires creating expressions dynamically that call different helper functions.
For static helper functions, compile-time safety can be guaranteed by using the following to get a MethodInfo instance:
var myMethodInfo = ((Func<int, int>) Helpers.MyStaticHelper.DoSomethingUseful).Method
Using this, if Helpers.MyStaticHelper.DoSomethingUseful were to change it's name or signature, this would result in a compile-time error. However, it only appears to work for static methods. Using a similar approach for non-static gives a CS0120 An object reference is required for the nonstatic field, method, or property 'Helpers.MyDynamicHelper.DoSomethingElse(int, int)'.
A workaround is possible by using something like this:
var myMethodInfo = typeof(Helpers.MyDynamicHelper).GetMethod("DoSomethingElse")
However this risks a runtime exception if DoSomethingElse is altered. I'm aware it's not possible to invoke the method without an instance, but these instances are needed for collecting and caching prerequisite data, so any instance created before executing the expression would be incorrect.
Is it possible to get a compile-time safe MethodInfo for the method without an instance?
You can use nameof to so ensure the method name is accurate:
var myMethodInfo = typeof(Helpers.MyDynamicHelper)
.GetMethod(nameof(Helpers.MyDynamicHelper.DoSomethingElse));

Invoke ToList() method using reflection at runtime in C#

I have a generic as follows.
public class PaginatedList<T> : List<T>
{...}
I simply want to invoke ToList() method on that object at runtime using reflection.
Can someone please help.
I have come so only far.
MethodInfo toListMethod = typeof(Enumerable).GetMethod("ToList");
var constructedToList = toListMethod.MakeGenericMethod(TypeObjectOfT);
constructedToList.Invoke(paginatedListObject, null);
I get exception at the last line with message, Parameter count mismatch. I feel that the first two steps are ok, as I have checked the toListMethod.ToString() and constructedToList.ToString(). And they have given me the following output, which I feel is correct.
System.Collections.Generic.List`1[TSource] ToList[TSource](System.Collections.Generic.IEnumerable`1[TSource])
System.Collections.Generic.List`1[AvbhHis.BL.Entities.PatientCategory] ToList[PatientCategory](System.Collections.Generic.IEnumerable`1[AvbhHis.BL.Entities.PatientCategory])
Questions:
1. Am I right so far?
What should be the parameter to MakeGenericMethod() method. In my case it is the Type of intance of the object of Type T at runtime.
There seems to be some problem with the Invoke method call. Is passing null correct as second parameter? The first parameter should be an object of the type PaginatedList right?
My energy is out, so kindly help.
The first parameter [to Invoke] should be an object of the type PaginatedList right?
ToList is a static method on Enumerable that takes an IEnumerable<T> as it's only parameter:
public static List<TSource> ToList<TSource>(
this IEnumerable<TSource> source
)
Invoke takes the instance as the first parameter and the method parameters after that. For a static method you use null for the "instance" parameter.
So the proper syntax would be
object o = constructedToList.Invoke(null, new object[] {paginatedListObject});
o will then be an object of type List<T> (but you don't know kniw what T is at compile time, so you can't cast it).
List<T> has a constructor that takes an IEnumerable<T> (Which gets called in ToList) so you can simplyfy this task by writing the following:
var resul = Activator.CreateInstance(typeof(List<>).MakeGenericType(TypeObjectOfT), paginatedListObject);

C# Reflection with generics

I have been searching for this for some time but not gotten anywhere. I want to do the following:
Given a type say Dictionary<string,MyClass> and say its method ContainsKey(string) - at run time
I want to be able to extract out the 'Generic signature' of this method. That is I want to get
Boolean Dictionary<TKey,TValue>.ContainsKey(TKey) ( and not Boolean ContainsKey(string) )
I know that this is possible by doing the following
var untyped_methods = typeof(DictObject).GetGenericTypeDefition().GetMethods();
// and extract the method info corresponding to ContainsKey
However is is possible to get this information directly from the methods reflected from the
actual type and not from the Generic types ? Meaning Can I get the generic definition
from methods that have been obtained as follows :
var actual_typed_methods = typeof(DictObject).GetMethods()
In essence is it possible to get the "un-typed" method signatures from MethodInfo objects returned in the second snippet above directly (not via comparing the lists and figuring out)
Thanks
EDIT: Maybe this did what you wanted. Not sure if you were actually looking to Invoke or not.
If Invoking, then no (see below). If you just want the definition, then you can use typeof(Dictionary<,>) to get the raw generic definitions.
Unfortunately, no if you are wanting to Invoke.
Demonstration of why:
void Main()
{
var genericDictionaryType = typeof(Dictionary<,>);
var method = genericDictionaryType.GetMethod("ContainsKey");
var dict = new Dictionary<string, string>();
dict.Add("foo", "bar");
Console.WriteLine("{0}", method.Invoke(dict, new[] { "foo" }));
}
Yields the following error:
Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Sounds simple enough to fix. Just call method.MakeGenericMethod(typeof(string)); to get the actual typed MethodInfo object.
Unfortunately again, you cannot.
Boolean ContainsKey(TKey) is not a GenericMethodDefinition. MakeGenericMethod may only be called on a method for which MethodBase.IsGenericMethodDefinition is true.
Reason for this is because the method defined as bool ContainsKey(TKey) instead of bool ContainsKey<TKey>(TKey).
You will need to get the ContainsKey method from the correct Dictionary<TK,TV> signature in order to use it.

How to Dynamically Create Method from String for Lambda Expression

My ultimate goal is to create a function that will dynamically pass method names to classes in the Hangfire library.
For example, here is the non-dynamic code which works:
RecurringJob.AddOrUpdate(() => myFunction(), Cron.Hourly)
The type of the first argument for AddOrUpdate is Expression<Action>. My first step was to use reflection to dynamically insert the function name:
Type thisControllerType = this.GetType();
MethodInfo method = thisControllerType.GetMethod(methodName); //methodName passed as string parameter
RecurringJob.AddOrUpdate(() => method.Invoke(this, null), Cron.Hourly);
When I check the Hangfire dashboard, it seems that this expression is being evaluated as MethodBase.Invoke. So I need help passing in the method name dynamically.
That may be enough info to answer my question, but another path I have taken is trying to generate the entire expression for the argument.
RecurringJob.AddOrUpdate(CreateCallExpression(method), Cron.Hourly);
public Expression<Action> CreateCallExpression(MethodInfo method)
{
//trying to pass in zero argument parameter, not sure if this syntax is correct
var parameter = System.Linq.Expressions.Expression.Parameter(typeof (Array));
return System.Linq.Expressions.Expression.Lambda<Action>(System.Linq.Expressions.Expression.Call(method, parameter));
}
In this case I am getting the exception {"Static method requires null instance, non-static method requires non-null instance.\r\nParameter name: method"}. I am working on that, but not sure if this is the road I should be going down. I have been working on this all day, so I was hoping someone might be able to help me speed up my learning.
Your second instance will work in creating a pointer to your specified method, but to solve your static issue you just need to modify the following line in one of 2 ways. First you can complete the static reference by declaring the method you seek to be static and modifying this line of code:
System.Linq.Expressions.Expression.Call(method, parameter);
You would have to provide a null parameter for the call method because if you are searching for a static method, then the compiler will know exactly what method signature you desire, because there will only exist 1. The line of code would be updated to:
System.Linq.Expressions.Expression.Call(null, method, parameter);
The second approach is to define the class or "instance" that correlates to the method so that the compiler knows what class to search against for the method signature. You would have to modify your code like this:
var myInstance = Expression.Parameter(typeof(MyClass), "inst");
System.Linq.Expressions.Expression.Call(myInstance, method, parameter)
I recommend looking at the documentation for Call so that you know exactly how the pointer is being created.

Route method call based on type parameter without reflection

I have a static method ConvertObject<T>(object obj) that converts object-based collections into generic collections. Based on the type of the obj parameter and the type parameter T, the method should return a specific type of collection. For instance, the call ConvertObject<List<string>>(obj), where obj is an ArrayList, would convert the ArrayList into a List<string>.
The problem is that method overloading cannot be used to distinguish between different T parameters, and the same object type (for instance ArrayList) could be converted to different output types based on different type parameters, so I cannot use regular method overloading either.
The best I've come up with is this, which calls ConvertToList<string>(ArrayList a) and returns a List<string>:
if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(List<>))
{
MethodInfo method = typeof(Serialization).GetMethod("ConvertToList", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo generic = method.MakeGenericMethod(typeof(T).GetGenericArguments());
return (T)generic.Invoke(null, new[]{(ArrayList)obj});
}
It works perfectly, however I am not comfortable with using reflection. I've seen using Performance Analysis that Type.GetMethod() and MethodInfo.MakeGenericMethod() take up a fair amount of CPU time, and also I would like to catch any misusages (such as calling with an array type parameter instead of a List<>) at compile-time, rather than on runtime.
Is there any way to do this using generics and static types, instead of runtime reflection type handling?
Please let me know if the problem description is confusing :)

Categories

Resources