System.Transactions integráció az SQL Serverrel
A .NET-keretrendszer 2.0-s verziója bevezetett egy tranzakciós keretrendszert, amely a System.Transactions névtéren keresztül érhető el. Ez a keretrendszer olyan módon teszi elérhetővé a tranzakciókat, hogy teljes mértékben integrálva legyen a .NET-keretrendszer, beleértve a ADO.NET.
A programozhatósági fejlesztések System.Transactions mellett a ADO.NET együttműködve koordinálhatja az optimalizálást a tranzakciók használatakor. A promóciós tranzakció egy egyszerű (helyi) tranzakció, amely automatikusan előléptethető egy teljesen elosztott tranzakcióra igény szerint.
A ADO.NET 2.0-tól kezdve támogatja a System.Data.SqlClient promóciós tranzakciókat az SQL Server használatakor. A promóciós tranzakció csak akkor hívja meg az elosztott tranzakciók többletterhelését, ha a többletterhelésre szükség van. A promóciós tranzakciók automatikusak, és nem igényelnek beavatkozást a fejlesztőtől.
A promóciós tranzakciók csak akkor érhetők el, ha a .NET-keretrendszer SQL Server-adatszolgáltatót (SqlClient
) használja az SQL Serverrel.
Promóciós tábla tranzakcióinak létrehozása
Az SQL Server .NET-keretrendszer-szolgáltatója támogatja a promóciós tranzakciókat, amelyeket a .NET-keretrendszer System.Transactions névtér osztályai kezelnek. A promóciós tranzakciók optimalizálják az elosztott tranzakciókat úgy, hogy elhalasztják az elosztott tranzakciók létrehozását, amíg szükség nem lesz rá. Ha csak egy erőforrás-kezelőre van szükség, nem történik elosztott tranzakció.
Feljegyzés
Részlegesen megbízható forgatókönyv esetén a DistributedTransactionPermission szükséges, ha egy tranzakciót előléptetnek egy elosztott tranzakcióban.
Promóciós tranzakciós forgatókönyvek
Az elosztott tranzakciók általában jelentős rendszererőforrásokat használnak fel, amelyeket a Microsoft Elosztott tranzakció koordinátora (MS DTC) felügyel, amely integrálja a tranzakcióban elért összes erőforrás-kezelőt. A promóciós tranzakció egy tranzakció speciális formája System.Transactions , amely hatékonyan delegálja a munkát egy egyszerű SQL Server-tranzakcióba. System.Transactions, System.Data.SqlClientés az SQL Server koordinálja a tranzakció kezelésével kapcsolatos munkát, és szükség szerint egy teljes elosztott tranzakcióra előlépteti.
A promóciós tranzakciók használatának előnye, hogy ha egy kapcsolat aktív TransactionScope tranzakcióval nyílik meg, és nem nyílik meg más kapcsolat, a tranzakció egyszerű tranzakcióként véglegesít, ahelyett, hogy a teljes elosztott tranzakció többletterhelésével jár.
Csatlakozás ion sztringszavai
A ConnectionString tulajdonság egy kulcsszót támogat, amely jelzi, Enlist
hogy észleli-e System.Data.SqlClient a tranzakciós környezeteket, és automatikusan bevonja a kapcsolatot egy elosztott tranzakcióba. Ha Enlist=true
, a kapcsolat automatikusan megjelenik a nyitó szál aktuális tranzakciós környezetében. Ha Enlist=false
a SqlClient
kapcsolat nem működik együtt elosztott tranzakcióval. Az alapértelmezett érték Enlist
igaz. Ha Enlist
nincs megadva a kapcsolati sztring, a rendszer automatikusan felvesz egy elosztott tranzakcióba, ha a kapcsolat megnyitásakor észlel egy kapcsolatot.
A Transaction Binding
kapcsolati sztring kulcsszavai SqlConnection szabályozzák a kapcsolat társítását egy bevont System.Transactions
tranzakcióval. A tulajdonságon keresztül TransactionBindingSqlConnectionStringBuilderis elérhető.
Az alábbi táblázat a lehetséges értékeket ismerteti.
Kulcsszó | Leírás |
---|---|
Implicit kötés feloldva | Az alapértelmezett érték. A kapcsolat megszűnik a tranzakciótól, és visszaáll az automatikus kapcsolódási módra. |
Explicit kötés megszüntetése | A kapcsolat a tranzakcióhoz marad csatolva, amíg a tranzakció be nem záródik. A kapcsolat sikertelen lesz, ha a társított tranzakció nem aktív vagy nem egyezik Current. |
A TransactionScope használata
Az TransactionScope osztály egy kódblokkot tranzakcióssá tesz úgy, hogy implicit módon bevonja a kapcsolatokat egy elosztott tranzakcióba. Mielőtt elhagyná, Complete meg kell hívnia a metódust a TransactionScope blokk végén. A blokk elhagyása meghívja a metódust Dispose . Ha olyan kivétel történt, amely miatt a kód elhagyja a hatókört, a tranzakció megszakítottnak minősül.
Javasoljuk, hogy használjon egy blokkot using
annak érdekében, hogy Dispose a blokk kilépésekor a rendszer meghívja TransactionScope az objektumot. A függőben lévő tranzakciók véglegesítésének vagy visszaállításának elmulasztása jelentősen károsíthatja a teljesítményt, mert az alapértelmezett időtúllépés egy TransactionScope perc. Ha nem használ utasítást using
, minden munkát el kell végeznie egy Try
blokkban, és explicit módon meg kell hívnia a Dispose blokk metódusát Finally
.
Ha kivétel történik a TransactionScopetranzakcióban, a tranzakció inkonzisztensként van megjelölve, és a rendszer megszakítja. A rendszer a megsemmisítéskor TransactionScope vissza fogja dobni. Ha nem történik kivétel, a részt vevő tranzakciók véglegesítésre kerülnek.
Feljegyzés
Az TransactionScope
osztály alapértelmezés szerint létrehoz egy tranzakciót Serializable
IsolationLevel. Az alkalmazástól függően érdemes lehet csökkenteni az elkülönítési szintet, hogy elkerülje a magas versengést az alkalmazásban.
Feljegyzés
Javasoljuk, hogy csak a frissítéseket, beszúrásokat és törléseket végezze el az elosztott tranzakciókon belül, mert jelentős adatbázis-erőforrásokat használnak fel. A kiválasztási utasítások szükségtelenül zárolhatják az adatbázis erőforrásait, és bizonyos esetekben előfordulhat, hogy tranzakciókat kell használnia a kiválasztásokhoz. Az adatbázison kívüli munkákat a tranzakció hatókörén kívül kell elvégezni, kivéve, ha más átjátszott erőforrás-kezelőket is érint. Bár a tranzakció hatókörének kivétele megakadályozza a tranzakció véglegesítését, az TransactionScope osztály nem rendelkezik a kód azon módosításainak visszaállítására, amelyek a tranzakció hatókörén kívül történtek. Ha a tranzakció visszagördülésekor valamilyen műveletet kell végrehajtania, meg kell írnia a IEnlistmentNotification felület saját implementációját, és explicit módon be kell jelentkeznie a tranzakcióba.
Példa
A használata System.Transactions megköveteli, hogy hivatkozzon System.Transactions.dll.
Az alábbi függvény bemutatja, hogyan hozhat létre promóciós tranzakciót két különböző SQL Server-példányon, amelyeket két különböző SqlConnection objektum jelöl, amelyek egy TransactionScope blokkba vannak csomagolva. A kód egy using
utasítással hozza létre a TransactionScope blokkot, és megnyitja az első kapcsolatot, amely automatikusan a TransactionScope. A tranzakció kezdetben egyszerű tranzakcióként van felvéve, nem pedig teljes elosztott tranzakcióként. A második kapcsolat csak akkor szerepel a TransactionScope listán, ha az első kapcsolat parancsa nem ad kivételt. A második kapcsolat megnyitásakor a rendszer automatikusan előlépteti a tranzakciót egy teljes elosztott tranzakcióra. A Complete rendszer meghívja a metódust, amely csak akkor véglegesíti a tranzakciót, ha nem történt kivétel. Ha a blokk bármely pontján kivétel történt, Complete
a TransactionScope rendszer nem hívja meg, és az elosztott tranzakció visszagördül, amikor a TransactionScope blokk végén using
el van adva.
// This function takes arguments for the 2 connection strings and commands in order
// to create a transaction involving two SQL Servers. It returns a value > 0 if the
// transaction committed, 0 if the transaction rolled back. To test this code, you can
// connect to two different databases on the same server by altering the connection string,
// or to another RDBMS such as Oracle by altering the code in the connection2 code block.
static public int CreateTransactionScope(
string connectString1, string connectString2,
string commandText1, string commandText2)
{
// Initialize the return value to zero and create a StringWriter to display results.
int returnValue = 0;
System.IO.StringWriter writer = new System.IO.StringWriter();
// Create the TransactionScope in which to execute the commands, guaranteeing
// that both commands will commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
try
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Create the SqlCommand object and execute the first command.
SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
// if you get here, this means that command1 succeeded. By nesting
// the using block for connection2 inside that of connection1, you
// conserve server and network resources by opening connection2
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
try
{
// The transaction is promoted to a full distributed
// transaction when connection2 is opened.
connection2.Open();
// Execute the second command in the second database.
returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
catch (Exception ex)
{
// Display information that command2 failed.
writer.WriteLine("returnValue for command2: {0}", returnValue);
writer.WriteLine("Exception Message2: {0}", ex.Message);
}
}
catch (Exception ex)
{
// Display information that command1 failed.
writer.WriteLine("returnValue for command1: {0}", returnValue);
writer.WriteLine("Exception Message1: {0}", ex.Message);
}
}
// If an exception has been thrown, Complete will not
// be called and the transaction is rolled back.
scope.Complete();
}
// The returnValue is greater than 0 if the transaction committed.
if (returnValue > 0)
{
writer.WriteLine("Transaction was committed.");
}
else
{
// You could write additional business logic here, notify the caller by
// throwing a TransactionAbortedException, or log the failure.
writer.WriteLine("Transaction rolled back.");
}
// Display messages.
Console.WriteLine(writer.ToString());
return returnValue;
}
' This function takes arguments for the 2 connection strings and commands in order
' to create a transaction involving two SQL Servers. It returns a value > 0 if the
' transaction committed, 0 if the transaction rolled back. To test this code, you can
' connect to two different databases on the same server by altering the connection string,
' or to another RDBMS such as Oracle by altering the code in the connection2 code block.
Public Function CreateTransactionScope( _
ByVal connectString1 As String, ByVal connectString2 As String, _
ByVal commandText1 As String, ByVal commandText2 As String) As Integer
' Initialize the return value to zero and create a StringWriter to display results.
Dim returnValue As Integer = 0
Dim writer As System.IO.StringWriter = New System.IO.StringWriter
' Create the TransactionScope in which to execute the commands, guaranteeing
' that both commands will commit or roll back as a single unit of work.
Using scope As New TransactionScope()
Using connection1 As New SqlConnection(connectString1)
Try
' Opening the connection automatically enlists it in the
' TransactionScope as a lightweight transaction.
connection1.Open()
' Create the SqlCommand object and execute the first command.
Dim command1 As SqlCommand = New SqlCommand(commandText1, connection1)
returnValue = command1.ExecuteNonQuery()
writer.WriteLine("Rows to be affected by command1: {0}", returnValue)
' If you get here, this means that command1 succeeded. By nesting
' the Using block for connection2 inside that of connection1, you
' conserve server and network resources by opening connection2
' only when there is a chance that the transaction can commit.
Using connection2 As New SqlConnection(connectString2)
Try
' The transaction is promoted to a full distributed
' transaction when connection2 is opened.
connection2.Open()
' Execute the second command in the second database.
returnValue = 0
Dim command2 As SqlCommand = New SqlCommand(commandText2, connection2)
returnValue = command2.ExecuteNonQuery()
writer.WriteLine("Rows to be affected by command2: {0}", returnValue)
Catch ex As Exception
' Display information that command2 failed.
writer.WriteLine("returnValue for command2: {0}", returnValue)
writer.WriteLine("Exception Message2: {0}", ex.Message)
End Try
End Using
Catch ex As Exception
' Display information that command1 failed.
writer.WriteLine("returnValue for command1: {0}", returnValue)
writer.WriteLine("Exception Message1: {0}", ex.Message)
End Try
End Using
' If an exception has been thrown, Complete will
' not be called and the transaction is rolled back.
scope.Complete()
End Using
' The returnValue is greater than 0 if the transaction committed.
If returnValue > 0 Then
writer.WriteLine("Transaction was committed.")
Else
' You could write additional business logic here, notify the caller by
' throwing a TransactionAbortedException, or log the failure.
writer.WriteLine("Transaction rolled back.")
End If
' Display messages.
Console.WriteLine(writer.ToString())
Return returnValue
End Function