2016-05-23 15 views
1

Ich habe ein ASP.Net WebAPI-Instanz-Setup, das eine MySQL-Datenbank für den Speicher verwendet. Ich habe eine ActionFilter geschrieben, die das Erstellen eines TransactionScope für die Lebensdauer einer einzelnen Endpunktanforderung behandelt.TransactionScope mit MySQL und verteilten Transaktionen

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; 
    } 
} 

Dann in den Endpunkten habe ich verschiedene Abfragen/Befehle, die zum Öffnen/Schließen Verbindungen der autoenlist=true Funktionalität von DbConnection ‚s mit. Ein Beispiel Endpunkt sein kann:

public async Task<IHttpActionResult> CreateStuffAsync() 
{ 
    var query = this.queryService.RetrieveAsync(); 

    // logic to do stuff 

    var update = this.updateService.Update(query); 

    return this.Ok(); 
} 

Ich mache keinen einzigen DbConnection und geben es um von oben, da dies ein einfaches Beispiel ist, wenn in der Praxis übergibt die Verbindung zwischen den Diensten eine große Refactoring erfordern würde (obwohl wenn nötig, kann dies getan werden). Ich lese auch, dass es besser ist, die Verbindungen nach Bedarf zu öffnen/zu schließen (d. H. Sie für so wenig Zeit wie möglich offen zu halten). Das queryService und updateService open/close DbConnection ‚s über using Aussagen:

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; 
} 

Die gleichen DbConnection ist in der Regel nicht für mehrere Abfragen innerhalb der gleichen API-Endpunkt-Anforderung verwendet - aber die gleiche Verbindungszeichenfolge ist.

Intermittently Ich sehe eine ausgelöste Ausnahme beim Versuch, die Verbindung zu öffnen:

"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()" 

Ich verstehe nicht, warum es versucht, die Transaktion zu einer verteilten Transaktion zu eskalieren, wenn alle Verbindungen sind gegen die gleiche Datenbank. Oder missverstanden/missbrauche ich die TransactionScope und DbConnection Instanzen?

+0

Bradly Grainger hat Recht. Es läuft gut. Ich benutze eine andere MySQL Verbindung anstelle von MySqlConnection.Ich vermute, dass MySqlConnection von Oracle nicht mehr entwickelt wird. Ich habe bestätigt, dass https://www.devart.com/dotconnect/mysql/ Ihr Problem lösen kann. Versuch es bitte. –

Antwort

1

Das Objekt bestimmt, ob es zu einer verteilten Transaktion eskalieren soll, basierend darauf, wie viele separate "Ressourcenmanager" (z. B. eine Datenbank) in die Transaktion aufgenommen wurden.

Es wird nicht zwischen Verbindungen zu verschiedenen physischen Datenbanken (die eine verteilte Transaktion erfordern) und mehreren MySqlConnection Verbindungen mit derselben Verbindungszeichenfolge und Verbindung zur gleichen Datenbank (die es möglicherweise nicht gibt) unterschieden. (Es wäre sehr schwierig für sie zu bestimmen, dass zwei separate "Ressourcenmanager" ① die gleiche physische DB repräsentieren und ② sequenziell und nicht parallel verwendet werden.) Wenn mehrere Objekte in einer Transaktion registriert werden, wird dies der Fall sein eskaliere immer zu einer verteilten Transaktion.

In diesem Fall führen Sie MySQL bug #70587, dass verteilte Transaktionen in Connector/NET nicht unterstützt werden.

Umgehungen wäre:

  1. nur ein MySqlConnection Achten Sie darauf, Objekt innerhalb eines TransactionScope geöffnet wird.
  2. Wechseln Sie zu einem separaten Connector, der verteilte Transaktionen unterstützt. Sie könnten MySqlConnector (NuGet, GitHub) als Drop-In-Ersatz für Connector/NET verwenden. Ich habe gehört, dass dotConnect for MySQL sie auch unterstützt (aber das nicht versucht habe).