我正在尝试在ASP.NET核心中实现一个控制器,该控制器返回从SQL数据库查询的潜在大量数据。为此,我通过Dapper的Query()方法使用buffered:false参数执行查询。这意味着必须在枚举查询结果之后处置SqlConnection对象。
似乎没有一个简单的方法可以实现这一目标。我通过编写一个包装IEnumerable的类使它起作用,并且引用计数了要求创建的任何Enumerator。处置所有枚举器后,它将处置数据库连接。
这似乎可行,但是我担心在某些情况下数据库连接将无法释放(例如,如果请求在开始枚举查询结果之前被取消)。是否有更好的方法,只需写入临时文件并将其流式传输到客户端?
答案 0 :(得分:0)
这是我最后想出的。连接的生存期由StreamedDatabaseObjectResult拥有,它代替通常的ObjectResult返回。存储库类返回要处置的连接以及结果。这似乎涵盖了所有情况,并且可以很好地与异步方法配合使用。
public class StreamedDatabaseObjectResult : ActionResult
{
private readonly Func<Task<DisposableEnumerable>> _getValuesFunc;
public StreamedDatabaseObjectResult(Func<Task<DisposableEnumerable>> getValuesFunc)
{
_getValuesFunc = getValuesFunc;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
using (var de = await _getValuesFunc())
{
var objectResult = new ObjectResult(de.Values);
await objectResult.ExecuteResultAsync(context);
}
}
}
public class DisposableEnumerable : IDisposable
{
public IEnumerable Values { get; }
private readonly IDisposable _disposable;
public DisposableEnumerable(IDisposable disposable, IEnumerable values)
{
Values = values;
_disposable = disposable;
}
/// <inheritdoc />
public void Dispose()
{
_disposable.Dispose();
}
}
[HttpGet]
[ProducesResponseType(200)]
public ActionResult<IEnumerable<MyClass>> GetThings(CancellationToken cancellationToken)
{
return new StreamedDatabaseObjectResult(() => _repo.GetThings(cancellationToken));
}