在Dapper中管理与非缓冲查询的连接
题
我最近开始使用 短小精悍, ,一切似乎都很好,很容易,但有一件事一直让我困惑:连接管理。
根据 文件:
Dapper并不管理连接的生命周期,它假定 它得到的连接是开放的,没有现有的datareaders枚举 (除非火星启用)
有鉴于此,我开始在我的存储库方法的实现中这样做:
using (var db = new SqliteConnection(connectionString)) {
// call Dapper methods here
}
然后我遇到了一个有大量记录的表,所以我想返回一个 IEnumerable<T>
通过传递 buffered: false
向 Query<>
方法,当我开始在前端枚举枚举枚举时,出现一个异常,说连接已关闭并被处置,这是预期的,因为我正在用前面的using块包装我的调用。
问题: 解决这个问题的最佳方法?
侧面问题: 我管理连接的方式是首选的方式吗?
解决方案
我会提供这个存储库模式:
public class Repository
{
private readonly string _connectionString;
public Repository(string connectionString)
{
_connectionString = connectionString;
}
protected T GetConnection<T>(Func<IDbConnection, T> getData)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
return getData(connection);
}
}
protected TResult GetConnection<TRead, TResult>(Func<IDbConnection, TRead> getData, Func<TRead, TResult> process)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
var data = getData(connection);
return process(data);
}
}
}
对于缓冲查询,您希望使用第一个重载 GetConnection
方法,对于非缓冲您使用第二,指定回调处理数据:
public class MyRepository : Repository
{
public MyRepository(string connectionString) : base(connectionString)
{
}
public IEnumerable<MyMapObject> GetData()
{
return GetConnection(c => c.Query<MyMapObject>(query));
}
public IEnumerable<ResultObject> GetLotsOfData(Func<IEnumerable<MyMapObject>, IEnumerable<ResultObject>> process)
{
return GetConnection(c => c.Query<MyMapObject>(query, buffered: false), process);
}
}
非常基本的用法:
static void Main(string[] args)
{
var repository = new MyRepository(connectionString);
var data = repository.GetLotsOfData(ProcessData);
}
public static IEnumerable<ResultObject> ProcessData(IEnumerable<MyMapObject> data)
{
foreach (var record in data)
{
var result = new ResultObject();
//do some work...
yield return result;
}
}
但请记住-在这种情况下,连接可能会打开太长时间。..
其他提示
@sergio,太棒了!谢谢你的这种伟大模式。我将其修改为异步,以便我可以使用Dapper的异步方法。使我的整个请求链异步,从控制器一直回到db!华丽!
public abstract class BaseRepository
{
private readonly string _ConnectionString;
protected BaseRepository(string connectionString)
{
_ConnectionString = connectionString;
}
// use for buffered queries
protected async Task<T> WithConnection<T>(Func<IDbConnection, Task<T>> getData)
{
try
{
using (var connection = new SqlConnection(_ConnectionString))
{
await connection.OpenAsync();
return await getData(connection);
}
}
catch (TimeoutException ex)
{
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
}
catch (SqlException ex)
{
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
}
}
// use for non-buffeed queries
protected async Task<TResult> WithConnection<TRead, TResult>(Func<IDbConnection, Task<TRead>> getData, Func<TRead, Task<TResult>> process)
{
try
{
using (var connection = new SqlConnection(_ConnectionString))
{
await connection.OpenAsync();
var data = await getData(connection);
return await process(data);
}
}
catch (TimeoutException ex)
{
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
}
catch (SqlException ex)
{
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
}
}
}
.
与如此如此:
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
// Assumes you have a Person table in your DB that
// aligns with a Person POCO model.
//
// Assumes you have an existing SQL sproc in your DB
// with @Id UNIQUEIDENTIFIER as a parameter. The sproc
// returns rows from the Person table.
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c =>
{
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(sql: "sp_Person_GetById", param: p, commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
. 不隶属于 StackOverflow