Вопрос
Каковы наилучшие методы выполнения транзакций в C # .Net 2.0?Какие классы следует использовать?На какие подводные камни следует обратить внимание и т.д.Вся эта ерунда с фиксацией и откатом.Я только начинаю проект, в котором мне может понадобиться выполнить некоторые транзакции при вставке данных в базу данных.Приветствуются любые ответы или ссылки даже на основные сведения о транзакциях.
Решение
Существует 2 основных вида транзакций;транзакции подключения и внешние транзакции.Транзакция подключения (например, SqlTransaction) привязана непосредственно к подключению к БД (например, SqlConnection), что означает, что в некоторых случаях вы должны продолжать передавать соединение по кругу - ОК, но не разрешает использование "создать / использовать / выпустить" и не допускает работу между БД.Пример (отформатированный с учетом пробела):
using (IDbTransaction tran = conn.BeginTransaction()) {
try {
// your code
tran.Commit();
} catch {
tran.Rollback();
throw;
}
}
Не слишком грязно, но ограничено нашим соединением "conn".Если мы хотим вызвать разные методы, теперь нам нужно передать "conn" по кругу.
Альтернативой является внешняя транзакция;новое в .NET 2.0, Транзакционный обзор объект (система.Transactions.dll) позволяет использовать в различных операциях (подходящие поставщики будут автоматически подключены к внешней транзакции).Это позволяет легко дорабатывать существующий (не транзакционный) код и общаться с несколькими поставщиками (хотя DTC будет задействован, если вы поговорите более чем с одним).
Например:
using(TransactionScope tran = new TransactionScope()) {
CallAMethodThatDoesSomeWork();
CallAMethodThatDoesSomeMoreWork();
tran.Complete();
}
Обратите внимание здесь, что два метода могут обрабатывать свои собственные соединения (open / use / close / dispose), но они будут незаметно становиться частью внешней транзакции без необходимости нам что-либо передавать.
Если в вашем коде будут ошибки, Dispose() будет вызван без Complete(), поэтому он будет откатан.Ожидаемая вложенность и т.д. Поддерживается, хотя вы не можете откатить внутреннюю транзакцию, пока не завершите внешнюю транзакцию:если кто-то недоволен, транзакция прерывается.
Другим преимуществом TransactionScope является то, что он привязан не только к базам данных;любой поставщик услуг, ориентированный на транзакции, может использовать его.WCF, например.Или есть даже некоторые объектные модели, совместимые с TransactionScope (т. е.Классы .NET с возможностью отката - возможно, проще, чем сувенир, хотя сам я никогда не использовал этот подход).
В общем, очень, очень полезный объект.
Несколько предостережений:
- На SQL Server 2000 TransactionScope немедленно перейдет в DTC;это исправлено в SQL Server 2005 и выше, он может использовать LTM (гораздо меньше накладных расходов), пока вы не обратитесь к 2 источникам и т.д., когда он повышен до DTC.
- Существует сбой это означает, что вам, возможно, потребуется изменить строку подключения
Другие советы
protected void Button1_Click(object sender, EventArgs e)
{
using (SqlConnection connection1 = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True"))
{
connection1.Open();
// Start a local transaction.
SqlTransaction sqlTran = connection1.BeginTransaction();
// Enlist a command in the current transaction.
SqlCommand command = connection1.CreateCommand();
command.Transaction = sqlTran;
try
{
// Execute two separate commands.
command.CommandText =
"insert into [doctor](drname,drspecialization,drday) values ('a','b','c')";
command.ExecuteNonQuery();
command.CommandText =
"insert into [doctor](drname,drspecialization,drday) values ('x','y','z')";
command.ExecuteNonQuery();
// Commit the transaction.
sqlTran.Commit();
Label3.Text = "Both records were written to database.";
}
catch (Exception ex)
{
// Handle the exception if the transaction fails to commit.
Label4.Text = ex.Message;
try
{
// Attempt to roll back the transaction.
sqlTran.Rollback();
}
catch (Exception exRollback)
{
// Throws an InvalidOperationException if the connection
// is closed or the transaction has already been rolled
// back on the server.
Label5.Text = exRollback.Message;
}
}
}
}
Вы также можете заключить транзакцию в собственную хранимую процедуру и обрабатывать ее таким образом, вместо того чтобы выполнять транзакции в самом C #.
если вам это нужно только для вещей, связанных с БД, некоторые OR Mappers (например, NHibernate) поддерживают трансактино из коробки по умолчанию.
Это также зависит от того, что вам нужно. Для базовых транзакций SQL вы можете попробовать выполнить транзакции TSQL, используя в своем коде BEGIN TRANS и COMMIT TRANS. Это самый простой способ, но он имеет сложность, и вы должны быть осторожны, чтобы зафиксировать его правильно (и выполнить откат).
Я бы использовал что-то вроде
SQLTransaction trans = null;
using(trans = new SqlTransaction)
{
...
Do SQL stuff here passing my trans into my various SQL executers
...
trans.Commit // May not be quite right
}
Любой сбой выведет вас прямо из using
, и транзакция всегда будет фиксироваться или откатываться (в зависимости от того, что вы скажете). Самая большая проблема, с которой мы столкнулись, заключалась в том, чтобы она всегда была совершенной. Использование гарантирует, что объем транзакции ограничен.