我读documentation for DbConnection.OpenAsync(CancellationToken)
,发现下面的代码片段:为什么DbConnection.OpenAsync(CancellationToken)的默认实现是同步的?
的默认实现调用同步Open调用和返回完成的任务。如果传递一个已经取消的cancellationToken,默认实现将返回一个取消的任务。 Open引发的异常将通过返回的Task Exception属性进行通信。现在
,如果我是一个参差不齐/慢速网络连接,并使用尚未覆盖DbConnection.OpenAsync(CancellationToken)
(即我用比其他System.Data.SqlClient
东西),和如果我屁股那到数据库提供商一个UI按钮的事件处理程序,如:基于我引用,如果连接了足够长的时间才能完成,我的表是“(无响应)”的文件上(假设的代码,未经测试)
async void button1_Clicked(object sender, EventArgs e)
{
using (var connection = MyProviderFactory.CreateConnection())
{
button1.Text = "Opening…";
connection.ConnectionString = _SomeString;
try
{
await connection.OpenAsync(); // Convenience wrapper around OpenAsync(CancellationToken)
button1.Text = "Opened successfully!";
}
catch (Exception ex)
{
button1.Text = ex.Message;
}
}
}
而连接如果提供者没有重写默认实现,则正在建立。为了防止这种情况发生,无论底层数据库提供者如何,我不妨做await Task.Run(async() => await connection.OpenAsync());
。为什么采用这种方式实现默认实现,以及如何在不编写提供程序感知代码的情况下知道何时需要Task.Run()
?
这种隐式事务管理API看起来使用起来很危险,并且喜欢某些事情以避免对我... – binki
啊,我明白了,你是说因为某些提供程序在打开连接时使用线程本地存储,所以' DbConnection' *不能*安全地使用线程池来使它立即返回。 – binki
@binki就像在Transaction.Current文档中说的那样,你通常不直接使用它。你使用'TransactionScope'来管理它,然后它变成一个非常有用的机制。连接类本身直接使用'Transaction.Current',如果在后台线程上调用,则不会看到预期的值。我没有提到使用线程本地存储的连接类本身(我只想到其他类),但是你是对的,那是另一种有效的可能性。 – hvd