Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Область применения: .NET Framework
.NET .NET
Standard
Платформа .NET включает новую платформу транзакций, к которой можно обращаться через пространство имен System.Transactions. Эта платформа предоставляет транзакции в полностью интегрированном с .NET виде, включая ADO.NET.
Помимо улучшенных способов программирования, System.Transactions и ADO.NET могут работать вместе для координации оптимизации при работе с транзакциями. Повышаемая транзакция — это упрощенная (локальная) транзакция, которая по необходимости может быть автоматически повышена до полностью распределенной транзакции.
Поставщик данных Microsoft SqlClient для SQL Server поддерживает повышаемые транзакции при работе с SQL Server. Повышаемая транзакция не влечёт дополнительных издержек, связанных с распределённой транзакцией, если только эти дополнительные издержки не требуются. Продвигаемые транзакции осуществляются автоматически и не требуют вмешательства разработчика.
Создание повышаемых транзакций
Поставщик данных Microsoft SqlClient для SQL Server поддерживает продвигаемые транзакции, которые реализуются с помощью классов в пространстве имен System.Transactions. Продвигаемые транзакции оптимизируют распределенные транзакции, откладывая создание распределенной транзакции до тех пор, пока она не понадобится. Если требуется только один диспетчер ресурсов, то распределенная транзакция не создается.
Примечание.
В среде с частичным доверием требуется DistributedTransactionPermission, когда транзакция повышается до уровня распределенной транзакции.
Сценарии продвигаемых транзакций
Как правило, для распределенных транзакций требуются значительные системные ресурсы, поскольку они управляются координатором распределенных транзакций (Майкрософт), который интегрирует все диспетчеры ресурсов, используемые в транзакции. Эскалируемая транзакция — это особая форма транзакции System.Transactions, которая фактически передаёт выполнение простой транзакции SQL Server. System.Transactions, Microsoft.Data.SqlClient и SQL Server координируют работу, выполняемую при обработке транзакции, при необходимости повышая ее до полностью распределенной транзакции.
Преимущество использования повышаемых транзакций заключается в том, что при открытии соединения с помощью активной транзакции TransactionScope и отсутствии других открытых соединений такая транзакция фиксируется как упрощенная, что позволяет избежать излишних затрат ресурсов на полностью распределенную транзакцию.
Ключевые слова в строке подключения
Свойство ConnectionString поддерживает ключевое слово Enlist, которое указывает, будет ли Microsoft.Data.SqlClient обнаруживать транзакционные контексты и автоматически включать подключение в распределенную транзакцию. Если Enlist=true, то соединение автоматически прикрепляется к текущему контексту транзакции открывающего потока. Если Enlist=false, то соединение SqlClient не будет взаимодействовать с распределенной транзакцией. Значение Enlist по умолчанию - true. Если ключевое слово Enlist в строке соединения не задано, то соединение автоматически прикрепляется к распределенной транзакции, если она будет обнаружена при открытии соединения.
Ключевые слова Transaction Binding в строке соединения SqlConnection управляют связью соединения с прикрепленной транзакцией System.Transactions . Оно также доступно через свойство TransactionBinding объекта SqlConnectionStringBuilder.
В следующей таблице описаны возможные значения.
| Ключевое слово | Описание |
|---|---|
| Неявная отмена привязки | По умолчанию. Когда транзакция завершается, соединение отсоединяется от нее и переключается обратно в режим автоматической фиксации. |
| Явное развязывание | Соединение остается привязанным к транзакции до закрытия транзакции. Соединение будет потеряно, если связанная с ним транзакция не активна или не соответствует Current. |
Использование TransactionScope
Класс TransactionScope делает блок кода транзакционным, неявно прикрепляя соединения к распределенной транзакции. В конце блока Complete перед тем, как выйти из него, необходимо вызвать метод TransactionScope . При выходе из этого блока вызывается метод Dispose . При возникновении исключения, в результате которого исполнение кода выходит за пределы области, транзакция считается прерванной.
Рекомендуется использовать блок using , чтобы гарантировать вызов метода Dispose для объекта TransactionScope при выходе из блока using. Неудачная попытка зафиксировать или откатить незавершенные транзакции может значительно снизить производительность, поскольку время ожидания по умолчанию для объекта TransactionScope составляет одну минуту. Если инструкция using не используется, необходимо явно выполнить все действия в блоке Try и вызвать метод Dispose в блоке Finally.
Если в объекте TransactionScopeвозникает исключение, транзакция помечается как несогласованная и прерывается. При удалении объекта TransactionScope будет произведен ее откат. Если исключений не возникает, то участвующие транзакции будут зафиксированы.
Примечание.
Класс TransactionScope по умолчанию создает транзакцию, в которой IsolationLevel имеет значение Serializable. В зависимости от особенностей вашего приложения, возможно, стоит рассмотреть снижение уровня изоляции, чтобы избежать высокой конкуренции за ресурсы.
Примечание.
В рамках распределенных транзакций рекомендуется выполнять только операции обновления, вставки и удаления, поскольку они потребляют значительные ресурсы базы данных. Инструкции выборки могут без необходимости блокировать ресурсы базы данных, и в некоторых случаях для выборки может потребоваться использование транзакций. Любые не связанные с базой данных действия должны выполняться за пределами области транзакции, если только в ней не задействованы другие диспетчеры ресурсов транзакций. Хотя исключение в области транзакции не позволяет зафиксировать транзакцию, класс TransactionScope не имеет возможности отката каких-либо изменений, внесенных кодом за пределами области самой транзакции. Если вам нужно выполнить какое-либо действие при откате транзакции, необходимо написать собственную реализацию интерфейса IEnlistmentNotification и явно зарегистрироваться в транзакции.
Пример
Для работы с System.Transactions необходима ссылка на файл System.Transactions.dll.
Следующая функция показывает, как создать транзакцию с возможностью повышения для работы с двумя разными экземплярами SQL Server, представленными двумя разными объектами SqlConnection, которые заключены в блок TransactionScope.
В приведенном ниже коде создается блок TransactionScope с инструкцией using и открывается первое соединение, которое автоматически прикрепляет транзакцию к блоку TransactionScope.
Изначально транзакция регистрируется как облегчённая транзакция, а не как полноценная распределённая транзакция. Второе соединение прикрепляется к транзакции TransactionScope , только если при выполнении команды в первом соединении не возникает исключения. При открытии второго соединения транзакция автоматически повышается до полностью распределенной.
Позднее вызывается метод Complete, который фиксирует транзакцию только в том случае, если не были созданы исключения. Если в любой точке блока TransactionScope возникло исключение, метод Complete вызван не будет и при удалении транзакции TransactionScope в конце ее блока using будет произведен откат распределенной транзакции.
using System;
using System.Transactions;
using Microsoft.Data.SqlClient;
class Program
{
static void Main(string[] args)
{
string connectionString = "Data Source = localhost; Integrated Security = true; Initial Catalog = AdventureWorks";
string commandText1 = "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')";
string commandText2 = "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')";
int result = CreateTransactionScope(connectionString, connectionString, commandText1, commandText2);
Console.WriteLine("result = " + result);
}
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;
}
}