using reflection execute generic method into list of type passed by string

Viewed 83

I need to based on string type - for example 'UserModel' execute method

  Task<IEnumerable<T>> QueryAsync<T>(string sqlStatment, DynamicParameters parameters, CommandType commandType = CommandType.StoredProcedure, int timeout = 90, string connectionId = "Default");

so I have

  string TypeName = "UserModel";
    Type type = Type.GetType("XXX.Shared.CoreClasses."+ TypeName+", XX.Shared")!;
    if(type is null) throw new ArgumentException(string.Format("type {0} not found", TypeName));


    IList l = (IList)Activator
    .CreateInstance(typeof(List<>)
    .MakeGenericType(type))!;

    System.Reflection.MethodInfo method = _sql.GetType().GetMethods().Where(c => c.Name == ("QueryAsync") 
        && c.GetParameters()[1].ParameterType == typeof(DynamicParameters)).First().MakeGenericMethod(new Type[] { type });

   object[] args = { SPname,d, CommandType.StoredProcedure,timeout };//not relevant

so now how to fill this l list with this method ?

like

 l= await method.Invoke(this, args)!; 
  • it yeals at me about casting / object canot be awaited ? how to do this properly ? is something like this slow ? or nowdays it does not matter until it will have to execute like xxxx times/sec ? thanks and regards !
1 Answers

The QueryAsync<T> method returns a Task<IEnumerable<T>> instance.

But the result of calling this method through reflection via method.Invoke(this, args) is type-cast as object, and since the type for the generic type parameter is given as a type name string, it is impossible here to cast directly to Task<IEnumerable<TypeIdentifier>> and await it, because the TypeIdentifier has to be known and specified in the source code at compile-time.

object task = method.Invoke(this, args)!;

Although, from the question it looks like the QueryAsync method is an instance of the type of the variable _sql. Therefore, possibly the correct way to invoke this method is perhaps:

object task = method.Invoke(_sql, args)!;

But how can we await the task then and get the result? Another small static helper method will solve this issue. We are calling it through reflection again, thus avoiding the impossible step of casting the object-typed QueryAsync<T> result as some Task<IEnumerable<TypeIdentifier>> in our source code.

private static async Task<IEnumerable> AwaitQueryAsyncResult<T>(Task<IEnumerable<T>> task)
    => await task;

Note the return type. It's a task returning IEnumerable (which works because IEnumerable<T> inherits IEnumerable). In some sense we basically "converted" a Task<IEnumerable<TypeIdentifier>> we are unable to specify in the source code to a Task<IEnumerable> - a concrete type that can be specified in the source code. No more generic type parameter anymore we don't know about at compile-time!

From here on, it's pretty much straightforward. We will call our little generic helper method AwaitQueryAsyncResult<T> through reflection in similar fashion as has been done with the QueryAsync<T> method:

...

object task = method.Invoke(this, args)!;


var miAwaitQueryAsyncResult = typeof(TheTypeWhereAwaitQueryAsyncResultIsDeclared)
    .GetMethod(nameof(AwaitQueryAsyncResult), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
    .MakeGenericMethod(type);

var resultAsEnumerable = await (Task<IEnumerable>) miAwaitQueryAsyncResult.Invoke(null, new[] { task } );

With QueryAsync<T>'s result now available as usefully typed resultAsEnumerable variable, it should be not too difficult to iterate/loop over it and add its elements to the list in the IList-typed variable l. I will leave the implementation of this little loop as a coffee-time exercise to the reader ;-)

Related