谁能建议一种将通用 C# Dapper 过程编写到 return n 结果集的优雅方法?
Can anyone suggest an elegant way of writing a generic C# Dapper procedure to return n result sets?
我使用 Dapper 创建了少量通用过程,以 return 列表或任何给定类型的单个项目(在 SQL 端使用 SP)用于 DI 模型数据服务, 例如
public async Task<List<TOut>> GetList<TOut>(string proc, dynamic parameters)
{
await using var conn = new SqlConnection(connectionString);
var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
return conn.Query<TOut>(cmd).ToList();
}
但是我一直在尝试扩展它以使用 QueryMultiple 方法 return 一个查询中的任意数量的结果集(为了提高效率,因为第一个查询非常昂贵并且其他结果集取决于在上面)。我希望在不编写大量代码的情况下得到强类型的结果集(不是匿名对象)。但是我看不出有什么方法可以想出任意数量的类型参数——到目前为止我能做的最好的事情就是为两个结果集生成一个方法,一个为三个结果集,依此类推:
public async Task<dynamic> Get2Lists<T1, T2>(string proc, dynamic parameters)
{
await using var conn = new SqlConnection(connectionString);
var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
var result = conn.QueryMultiple(cmd);
return new
{
Table1 = result.Read<T1>(),
Table2 = result.Read<T2>()
};
}
public async Task<dynamic> Get3Lists<T1, T2, T3>(string proc, dynamic parameters)
{
await using var conn = new SqlConnection(connectionString);
var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
var result = conn.QueryMultiple(cmd);
return new
{
Table1 = result.Read<T1>(),
Table2 = result.Read<T2>(),
Table3 = result.Read<T3>()
};
}
在我看来必须有更优雅的方式 - 有什么建议吗?
简短回答:Dapper 支持使用 SqlMapper.GridReader
。
下面是我们如何使用它。您应该能够修改它以满足您的需要。我们有一个 DapperRepository
class 来容纳我们所有的通用 dapper 调用。这是一个(大大)缩短的版本,向您展示您所需要的。
using Dapper;
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
public class DapperRepository
{
private readonly string _connStringName;
public DapperRepository(string connStringName)
{
_connStringName = connStringName;
}
private SqlConnection OpenConnection(string cs)
{
var connection = ConfigurationManager.ConnectionStrings[cs].ConnectionString;
SqlConnection con = new SqlConnection(connection);
con.Open();
return con;
}
// lots of other methods removed for the brevity of this post
// This is the method you want
public void QuerySPMultiple(string sql, Action<SqlMapper.GridReader> callback, object parameters = null)
{
using (var connection = OpenConnection(_connStringName))
{
var gr = connection.QueryMultiple(sql, param: parameters,commandTimeout:0, commandType: CommandType.StoredProcedure);
callback(gr);
}
}
}
假设我们有一个 class 看起来像这样:
public class FooBar
{
public List<Stuff> MyStuff { get; set; }
public int MyInt { get; set; }
}
然后用 returns 多个数据集的单个存储过程调用填充 FooBar
:
// Create an instance of the repository
DapperRepository repo = new DapperRepository("your connection string");
// Create your parameters
DynamicParameters param = new DynamicParameters();
param.Add("@Name", "Value");
// Call the stored proc and get back multiple sets of data
FooBar fooBar = new FooBar();
repo.QuerySPMultiple("YourSPName", (reader) =>
{
fooBar.MyStuff = reader.Read<Stuff>().ToList();
fooBar.MyInt = reader.Read<int>().FirstOrDefault();
}, parameters: param);
我希望所有这些都有意义!如果没有,请随时 post 跟进问题。
我使用 Dapper 创建了少量通用过程,以 return 列表或任何给定类型的单个项目(在 SQL 端使用 SP)用于 DI 模型数据服务, 例如
public async Task<List<TOut>> GetList<TOut>(string proc, dynamic parameters)
{
await using var conn = new SqlConnection(connectionString);
var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
return conn.Query<TOut>(cmd).ToList();
}
但是我一直在尝试扩展它以使用 QueryMultiple 方法 return 一个查询中的任意数量的结果集(为了提高效率,因为第一个查询非常昂贵并且其他结果集取决于在上面)。我希望在不编写大量代码的情况下得到强类型的结果集(不是匿名对象)。但是我看不出有什么方法可以想出任意数量的类型参数——到目前为止我能做的最好的事情就是为两个结果集生成一个方法,一个为三个结果集,依此类推:
public async Task<dynamic> Get2Lists<T1, T2>(string proc, dynamic parameters)
{
await using var conn = new SqlConnection(connectionString);
var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
var result = conn.QueryMultiple(cmd);
return new
{
Table1 = result.Read<T1>(),
Table2 = result.Read<T2>()
};
}
public async Task<dynamic> Get3Lists<T1, T2, T3>(string proc, dynamic parameters)
{
await using var conn = new SqlConnection(connectionString);
var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
var result = conn.QueryMultiple(cmd);
return new
{
Table1 = result.Read<T1>(),
Table2 = result.Read<T2>(),
Table3 = result.Read<T3>()
};
}
在我看来必须有更优雅的方式 - 有什么建议吗?
简短回答:Dapper 支持使用 SqlMapper.GridReader
。
下面是我们如何使用它。您应该能够修改它以满足您的需要。我们有一个 DapperRepository
class 来容纳我们所有的通用 dapper 调用。这是一个(大大)缩短的版本,向您展示您所需要的。
using Dapper;
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
public class DapperRepository
{
private readonly string _connStringName;
public DapperRepository(string connStringName)
{
_connStringName = connStringName;
}
private SqlConnection OpenConnection(string cs)
{
var connection = ConfigurationManager.ConnectionStrings[cs].ConnectionString;
SqlConnection con = new SqlConnection(connection);
con.Open();
return con;
}
// lots of other methods removed for the brevity of this post
// This is the method you want
public void QuerySPMultiple(string sql, Action<SqlMapper.GridReader> callback, object parameters = null)
{
using (var connection = OpenConnection(_connStringName))
{
var gr = connection.QueryMultiple(sql, param: parameters,commandTimeout:0, commandType: CommandType.StoredProcedure);
callback(gr);
}
}
}
假设我们有一个 class 看起来像这样:
public class FooBar
{
public List<Stuff> MyStuff { get; set; }
public int MyInt { get; set; }
}
然后用 returns 多个数据集的单个存储过程调用填充 FooBar
:
// Create an instance of the repository
DapperRepository repo = new DapperRepository("your connection string");
// Create your parameters
DynamicParameters param = new DynamicParameters();
param.Add("@Name", "Value");
// Call the stored proc and get back multiple sets of data
FooBar fooBar = new FooBar();
repo.QuerySPMultiple("YourSPName", (reader) =>
{
fooBar.MyStuff = reader.Read<Stuff>().ToList();
fooBar.MyInt = reader.Read<int>().FirstOrDefault();
}, parameters: param);
我希望所有这些都有意义!如果没有,请随时 post 跟进问题。