1

I'm trying to do some code more generic with Dapper and stored procedures. I've made some research, but I got stuck on this part..

I have some classes that act like entities that will be returned by some stored procedures (those procedures can't be modified).

For example, I have these two classes:

public class User 
{
     public int Id { get; set; }
     public string Username { get; set; }
     public string Email { get; set; }
}

public class Role 
{
     public int Id { get; set; }
     public string Name { get; set; }
     public string Description { get; set; }
}

The stored procedures usually return some select clauses, for this example it returns two select clauses, each for User and Role's information, so I have the following class to store those..

public class UserAndRoleInfo 
{
     public IEnumerable<User> users { get; set; }
     public IEnumerable<Role> roles { get; set;}
}

In this project we have to use Dapper, so I'm doing a generic function where T is the class that will be returned, this class has the same structure as shown above and could have two or more entities as attributes.

The main idea is to get the properties of T, then for each property get the value returned by the select clause cast as property type, and finally add this property to the class instance which will be returned.

public async Task<T> ExecuteStoredProcedureMultiQuery<T>(string cnx, string storedProcedure, object param = null) where T : class, new()
{
     using (var conn = new SqlConnection(cnx))
     {
         conn.Open();
         var result = param == null
                ? await conn.QueryMultipleAsync(
                    storedProcedure,
                    commandType: CommandType.StoredProcedure
                ).ConfigureAwait(false)
                : await conn.QueryMultipleAsync(
                    storedProcedure,
                    param: param,
                    commandType: CommandType.StoredProcedure
                ).ConfigureAwait(false);

         T res = new T();
         Type t = typeof(T);

         PropertyInfo[] propInfos = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);

         foreach(PropertyInfo propInfo in propInfos)
         {
             // I want to do something like this 
             propInfo.PropertyType propValue = result.Read<propInfo.PropertyType.GetEnumUnderlyingType()>();
             propInfo.SetValue(res, propValue);
         }

         conn.Close();
         conn.Dispose();
          
         return res;
     }
}

The problem here is that I'm not sure how to get this

propInfo.PropertyType propValue = result.Read<propInfo.PropertyType.GetEnumUnderlyingType()>();

I was working around with Activator.CreateInstance(propInfo.PropertyType), but I'm not sure how to implement it in this case.

If something is unclear or more information is needed, I'm here to answer. Thanks in advance.

Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
  • 2
    Does this answer your question? [How do I use reflection to call a generic method?](https://stackoverflow.com/questions/232535/how-do-i-use-reflection-to-call-a-generic-method) – Johnathan Barclay Apr 28 '21 at 15:05
  • @JohnathanBarclay Could you tell me how could I apply that in this case? – Héctor Marin Apr 28 '21 at 15:08
  • He's saying that because you show `result.Read` in your "something like this" code, which you cannot do and the linked question addresses. – Connor Low Apr 28 '21 at 15:10
  • @ConnorLow, That's right, I just was wondering how can I achieve that in this case.. because I couldn't figure out how to make it works – Héctor Marin Apr 28 '21 at 15:14
  • Apply [this answer](https://stackoverflow.com/a/232621/6789816) to `result.Read`. Start with `result.GetType().GetMethod(nameof(result.Read)); ....` – Connor Low Apr 28 '21 at 15:18
  • There is no need to Cloase and Dispose a connection in a using statement like that. Using+IDisposable takes care of it. – Palle Due Apr 29 '21 at 07:13

1 Answers1

0

Using the answer here as reference, you could do this:

foreach (var propInfo in propInfos)
{
    var method = result.GetType().GetMethod(nameof(result.Read));
    var generic = method.MakeGenericMethod(propInfo.PropertyType.GetEnumUnderlyingType());
    var propValue = generic.Invoke(result, null);
    propInfo.SetValue(res, propValue);
}
Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35