c# – 使用异步时不处理SqlConnection
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c# – 使用异步时不处理SqlConnection,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含4904字,纯文字阅读大概需要8分钟。
内容图文
![c# – 使用异步时不处理SqlConnection](/upload/InfoBanner/zyjiaocheng/904/8c952cd03255466b863388a6ea17f204.jpg)
我有一个项目,它有一个Sql-Server数据库后端和Dapper作为ORM.我试图使用Dapper的QueryAsync()方法来获取一些数据.不仅如此,对我的repo的调用来自于使用Task.WhenAll调用的几个任务(也就是说,每个任务都涉及从该repo获取数据,因此每个任务都等待我的repo包装QueryAsync的方法()致电).
问题是我的SqlConnections永远不会关闭,即使我使用的是使用块.因此,我有100个与我的数据库的开放连接,并最终开始获得“达到最大池大小”的异常.问题是,当我切换到Query()而不是QueryAsync()时,它工作正常,但我希望能够异步执行此操作.
这是一个代码示例.我试图尽可能地模仿实际应用程序的结构,这就是为什么它看起来比它必须更复杂.
接口:
public interface IFooRepository<T> where T: FooBase
{
Task<IEnumerable<T>> Select(string account, DateTime? effectiveDate = null);
}
执行:
public class FooRepository : RepositoryBase, IFooRepository<SpecialFoo>
{
private readonly IWebApiClientRepository _accountRepository;
public FooRepository(IWebApiClientRepository repo)
{
_accountRepository = repo;
}
public async Task<IEnumerable<FuturePosition>> Select(string code, DateTime? effectiveDate = null)
{
effectiveDate = effectiveDate ?? DateTime.Today.Date;
var referenceData = await _accountRepository.GetCrossRefferenceData(code, effectiveDate.Value);
using (var connection = new SqlConnection("iamaconnectionstring")
{
connection.Open();
try
{
var res = await connection.QueryAsync<FuturePosition>(SqlQueryVariable + "AND t.code = @code;",
new
{
effectiveDate = effectiveDate.Value,
code = referenceData.Code
});
foreach (var item in res)
{
item.PropFromReference = referenceData.PropFromReference;
}
return res;
}
catch (Exception e)
{
//log
throw;
}
finally
{
connection.Close();
}
}
}
}
所以现在使用调用代码,有2层.我将从外部开始.我认为这就是问题所在.以下是评论.
填充器:
public class Populator : PopulatorBase
{
private IAccountRepository _acctRepository;
public override async Task<IEnumerable<PopulationResult>> ProcessAccount(DateTime? popDate = null)
{
//My attempt at throttling the async calls
//I was hoping this would force a max of 10 simultaneous connections.
//It did not work.
SemaphoreSlim ss = new SemaphoreSlim(10,10);
var accountsToProcess = _acctRepository.GetAllAccountsToProcess();
var accountNumbers = accountsToProcess.SelectMany(a => a.accountNumbers).ToList();
List<Task<ProcessResult>> trackedTasks = new List<Task<ProcessResult>>();
foreach (var item in accountNumbers)
{
await ss.WaitAsync();
trackedTasks.Add(ProcessAccount(item.AccountCode, popDate ?? DateTime.Today));
ss.Release();
}
//my gut tells me the issue is because of these tasks
var results = await Task.WhenAll(trackedTasks);
return results;
}
private async Task<ProcessResult>ProcessAccount(string accountCode, DateTime? popDate)
{
var createdItems = await _itemCreator.MakeExceptions(popDate, accountCode);
return Populate(accountCode, createdItems);
}
}
ItemCreator:
public class ItemCreator : ItemCreatorBase
{
private readonly IFooRepository<FuturePosition> _fooRepository;
private readonly IBarRepository<FuturePosition> _barRepository;
public RussellGlobeOpFutureExceptionCreator() )
{
//standard constructor stuff
}
public async Task<ItemCreationResult> MakeItems(DateTime? effectiveDate, string account)
{
DateTime reconDate = effectiveDate ?? DateTime.Today.Date;
//this uses the repository I outlined above
var foos = await _fooRepository.Select(account, effectiveDate);
//this repository uses a rest client, I doubt it's the problem
var bars = await _barRepository.Select(account, effectiveDate);
//just trying to make this example less lengthy
var foobars = MakeFoobars(foos, bars);
var result = new ItemCreationResult { EffectiveDate = effectiveDate, Items = foobars };
return result;
}
}
就我的尝试而言:
>使用SemaphoreSlim进行节流
>没有节流
>在repo中使用connection.OpenAnync()
>包括/排除finally块(应该与使用无关)
值得知道,populator中的foreach循环运行大约500次.基本上,有500个帐户的列表.对于每一个,它需要执行一个长期运行的填充任务,其中涉及从我的Foo仓库中提取数据.
老实说,我不知道.我认为这可能与等待来自populator中该任务列表中的每个任务的异步数据库调用有关.对这个问题的任何见解都会非常有帮助.
解决方法:
经过一番挖掘,我想我已经设法解决了这个问题.我认为我实际上并没有像我原先假设的那样遇到连接泄漏.从我现在的理解,对于连接池,当SQL连接从代码关闭时,它实际上不会消失 – 它只是作为空闲连接进入连接池.查看SQL中的开放连接仍将显示它.
由于我的数据访问是异步的,所以在任何“关闭”连接之前打开的所有连接都返回到池,这意味着为每个请求打开了一个新连接.这导致了我看到的令人吃惊的打开连接数,让我觉得我有连接泄漏.
使用SemaphoreSlim实际上解决了这个问题 – 我只是错误地实现了它.它应该像这样工作:
public override async Task<IEnumerable<ProcessResult>> ProcessAccount(DateTime? popDate = null)
{
foreach (item in accountNumbers)
{
trackedTasks.Add(new Func<Task<ProcessResult>>(async () =>
{
await ss.WaitAsync().ConfigureAwait(false);
try
{
return await ProcessAccount(item.AccountCode, popDate ?? DateTime.Today).ConfigureAwait(false);
}
catch (Exception e)
{
//log, etc.
}
finally
{
ss.Release();
}
})());
}
}
这样做会限制一次打开的连接数量,并等待它们关闭,因此池中的相同较小的连接组将被重新使用.
内容总结
以上是互联网集市为您收集整理的c# – 使用异步时不处理SqlConnection全部内容,希望文章能够帮你解决c# – 使用异步时不处理SqlConnection所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。