[英]TransactionScope with MySQL and Distributed Transactions
I have an ASP.Net WebAPI instance setup that uses a MySQL database for storage. 我有一个使用MySQL数据库进行存储的ASP.Net WebAPI实例设置。 I have written an
ActionFilter
that handles creating a TransactionScope
for the lifetime of a single endpoint request. 我编写了一个
ActionFilter
,用于处理在单个端点请求的生存ActionFilter
创建TransactionScope
的问题。
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
var transactionScopeOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted };
using (var transaction = new TransactionScope(TransactionScopeOption.RequiresNew, transactionScopeOptions, TransactionScopeAsyncFlowOption.Enabled))
{
var handledTask = await continuation();
transaction.Complete();
return handledTask;
}
}
Then throughout the endpoints I have different queries/commands that open/close connections using the autoenlist=true
functionality of DbConnection
's. 然后在整个端点上,我有不同的查询/命令,它们使用
DbConnection
的autoenlist=true
功能打开/关闭连接。 An example endpoint may be: 示例端点可能是:
public async Task<IHttpActionResult> CreateStuffAsync()
{
var query = this.queryService.RetrieveAsync();
// logic to do stuff
var update = this.updateService.Update(query);
return this.Ok();
}
I don't create a single DbConnection
and pass it around from the top, as this is a simplistic example, when in practise passing the connection between services would require a large refactor (although if necessary, this can be done). 我不创建单个
DbConnection
并从顶部传递它,因为这是一个简单的示例,当在实践中在服务之间传递连接时将需要大量的重构(尽管如有必要,也可以这样做)。 I also read that it is better to open/close the connections as necessary (ie keep them open for as little time as possible). 我还读到,最好根据需要打开/关闭连接(即,保持打开状态的时间尽可能短)。 The
queryService
and updateService
open/close DbConnection
's via using
statements: queryService
和updateService
通过using
语句打开/关闭DbConnection
:
var factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient");
using (var connection = factory.CreateConnection())
{
connection.ConnectionString = "Data Source=localhost;Initial Catalog=MyDatabase;User ID=user;Password=password;Connect Timeout=300;AutoEnlist=true;";
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
var result = await connection.QueryAsync(Sql).ConfigureAwait(false);
return result;
}
The same DbConnection
is generally not used for multiple queries within the same API endpoint request -- but the same connection string is. 通常,相同的
DbConnection
不会用于同一API端点请求内的多个查询-而是使用相同的连接字符串。
Intermittently I am seeing an exception thrown when attempting to open the connection: 间歇性地打开尝试打开连接时看到异常:
"ExceptionType": "System.NotSupportedException",
"ExceptionMessage": "System.NotSupportedException: MySQL Connector/Net does not currently support distributed transactions.\r\n at MySql.Data.MySqlClient.ExceptionInterceptor.Throw(Exception exception)\r\n at MySql.Data.MySqlClient.MySqlConnection.EnlistTransaction(Transaction transaction)\r\n at MySql.Data.MySqlClient.MySqlConnection.Open()"
I do not understand why it is attempting to escalate the transaction to a distributed transaction, when all of the connections are against the same database. 我不明白当所有连接都针对同一个数据库时,为什么它试图将事务升级为分布式事务。 Or am I misunderstanding/misusing the
TransactionScope
and DbConnection
instances? 还是我误解/滥用了
TransactionScope
和DbConnection
实例?
The System.Transactions.Transaction
object makes the determination of whether to escalate to a distributed transaction based on how many separate "resource managers" (eg, a database) have enlisted in the transaction. System.Transactions.Transaction
对象基于已在事务中登记了多少个单独的“资源管理器”(例如,数据库)来确定是否升级为分布式事务。
It does not draw a distinction between connections to different physical databases (that do require a distributed transaction) and multiple MySqlConnection
connections that have the same connection string and connect to the same database (which might not). 它不会在到不同物理数据库的连接(确实需要分布式事务)与具有相同连接字符串并连接到同一数据库的多个
MySqlConnection
连接(可能没有)之间进行区分。 (It would be very difficult for it to determine that two separate "resource managers" ① represent the same physical DB and ② are being used sequentially, not in parallel.) As a result, when multiple MySqlConnection
objects enlist in a transaction, it will always escalate to a distributed transaction. (很难确定两个单独的“资源管理器”①代表同一物理数据库,而②正在依次而不是并行使用)。结果,当多个
MySqlConnection
对象加入一个事务时,它将始终升级为分布式事务。
When this happens, you run into MySQL bug #70587 that distributed transactions aren't supported in Connector/NET. 发生这种情况时,您会遇到MySQL错误#70587 ,该错误表明Connector / NET中不支持分布式事务。
Workarounds would be: 解决方法是:
MySqlConnection
object is opened within any TransactionScope
. TransactionScope
仅打开一个MySqlConnection
对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.