Générer la requête Dapper dans un délégué, Func ou Action C #

c#-4.0 dapper generics

Question

J'ai plusieurs types de requêtes Dapper comme celle-ci ci-dessous, avec plusieurs types différents. Ceci est un spécifique de ceux-ci, qui produisent par exemple la liste <ClassA>:

string anSql = GetSqlQueryText("query_name");
SqlConnection connection = GetSqlConnection();

List<ClassA> result = null;
try
{
    connection.Open();
    result = connection.Query<ClassA>(anSql, new    //want to move this part from here
    {
        param1 = "value1",
        param2 = "value2"
    }).ToList();                                    //to here out, to an outer call
}
catch //more error handling and retry logic omitted and this is a simplified version
{
    result = new List<ClassA>();       //this good to be filled up by the generic type
}
finally
{
    connection.Close();
}

Je veux joindre ce type de requêtes à une méthode générique GenDapperQuery <T>, qui peut être appelée à l'aide d'un délégué (ou Func / Action ou autre) de la sorte (T sera ClassA ou ClassB, etc. le code final):

List<T> result = GenDapperQuery<T>(() =>
{
    result = connection.Query<T>(anSql, new
    {
        param1 = "value1",
        param2 = "value2"
    }).ToList();
}
//and I want to use the result then as a specific type e.g. ClassA
//either immediately or after a cast
result[0].Id = 3; //or
(result as List<ClassA>)[0].Id = 3;

Donc, mon but est d'utiliser la connexion, ma logique de gestion des erreurs / nouvelle tentative, et bien sûr la requête Dapper en général, car je ne veux pas les écrire autant que la requête et le type que j'ai, mais je veux à dire à cette méthode générique (voulue) d'une manière ou d'une autre, ce qu'il faut interroger avec dapper et quel type de liste (générique) créer et remplir.

(Cette méthode générique (désirée) sera dans la même classe où je peux créer une connexion une fois. La partie gestion des erreurs sera plus compliquée, mais toujours la même pour tous les types, c'est pourquoi je ne veux pas les écrire plusieurs fois Les paramètres peuvent varier librement comme une chaîne SQL en entrée.)

Mon problème maintenant, c'est que je ne peux pas écrire une requête Dapper générique entourant mon propre code, mais avec une fonction injectée spécifique en dehors de cette méthode (souhaitée).

Est-ce possible en C #? Toute suggestion serait hautement appréciée.

Réponse acceptée

Il y a plusieurs façons d'accomplir ceci. Un mécanisme consiste à créer une méthode pour Execute / ErrorHandler:

public TResult ExecuteWrapper<TResult>(SqlConnection connection, Func<TResult, SqlConnection> func)
{
    TResult result;
    try
    {
        connection.Open();
        // Query will be wrapped in a function or lambda
        result = func(connection);
    }
    catch //more error handling and retry logic omitted and this is a simplified version
    {
        // Specifying a new TResult may be more difficult. You could either:
        // 1. Pass a default value in the method parameter
        //    result = defaultValue; // defaultValue is method parameter
        // 2. Use System.Activator to create a default instance
        //    result = (TResult)System.Activator(typeof(TResult));

        // Original: result = new List<ClassA>(); // this good to be filled up by the generic type
    }
    finally
    {
        connection.Close();
    }
    return result;
}

Ensuite, vous l'utiliseriez comme ceci:

List<ClassA> result = ExecuteWrapper(connection, (cn) =>
    {
        string anSql = GetSqlQueryText("query_name");
        return cn.Query<ClassA>(anSql, new
            {
                param1 = "value1",
                param2 = "value2"
            }).ToList();        
    });

Réponse populaire

Je poste mon amélioration sur la base de la bonne réponse de Ryan - pour que ces solutions soient également réunies. Cette fonction wrapper demande non seulement la connexion SQL, mais aussi le nom de la requête. Cette solution est donc plus courte avec une ligne du côté appelant et peut-être un peu plus élégante.
La fonction wrapper modifiée:

public TResult ExecuteWrapper<TResult>(SqlConnection connection, string queryName, Func<SqlConnection, string, TResult> func)
{
   string anSql = GetSqlText(queryName);
   TResult result;
   try
   {
      connection.Open();
      result = func(connection, anSql);
   }
   catch
   {
      result = System.Activator.CreateInstance<TResult>(); //this is working in .NET 4.5 environment
   }
   finally
   {
      connection.Close();
   }
   return result;
}

Et le côté appelant de ceci:

List<ClassA> result = ExecuteWrapper(connection, "query_name", (conn, sql) =>
    {
        return conn.Query<ClassA>(sql, new
            {
                param1 = "value1",
                param2 = "value2"
            }).ToList();        
    });

Merci encore à Ryan pour sa réponse.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi