在委托,Func或Action C#中概括Dapper查询

时间:2013-07-19 19:26:22

标签: c#-4.0 generics dapper

我有几种Dapper查询,如下所示,结果有几种不同的类型。这是这些中特有的一种,它们产生例如列表与LT; 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();
}

我想将这种查询加入GenDapperQuery&lt; T&gt;泛型方法,可以在委托(或Func / Action或其他任何东西)的帮助下调用(最终代码中的T将是ClassA或ClassB等):

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;

所以我的目的是使用连接,我的错误处理/重试逻辑,当然还有Dapper查询一般,多次(因为我不想把它们写下来像查询和类型一样多),但是我想以某种方式对这个(想要的)通用方法说什么,用dapper查询什么以及什么类型的(通用)列表创建和填充。

(这个(想要的)泛型方法将在同一个类中,我可以创建一次连接。错误处理部分将更复杂,但在每种类型总是相同,这就是为什么我不想写它们多次下调。参数可以自由变化,就像sql字符串作为输入一样。)

我现在的问题是,我无法编写围绕我自己的代码的通用Dapper查询,但是使用来自此(通缉)方法之外的特定注入函数。

这在C#中是否可行?任何建议都将受到高度赞赏。

2 个答案:

答案 0 :(得分:3)

有很多方法可以实现这一目标。一种机制是为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;
}

然后你会像这样使用它:

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

答案 1 :(得分:2)

我发布了我的改进 - 基于Ryan的好答案 - 同时也将这些解决方案结合在一起。此包装函数不仅请求SQL连接,还请求查询的名称。所以这个解决方案更短,在呼叫方面有一条线,也许更优雅 修改后的包装函数:

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

这是来电者:

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

再次感谢Ryan的回答。