Я пытаюсь использовать System.Transactions (TransactionScope) для координации набора процессов, каждый из которых выполняет некоторую работу с базой данных. В конечном итоге все процессы должны фиксироваться или откатываться атомарно через один родительский процесс. К сожалению, пока ничего из того, что я пробовал, не работает.
Моя основная стратегия - использовать TransactionScope в родительском процессе, сохранить его в файл и вызвать дочерний процесс, который загружает файл, использует транзакцию внутри своего собственного TransactionScope и возвращается к родительскому процессу.
Но у меня это не работает. Когда я возвращаюсь после вызова первого дочернего элемента, я иногда вижу, что родительская транзакция была помечена как прерванная. При попытке клонирования возникает исключение TransactionAbortedException.
Я также видел исключения, когда второй ребенок пытается десериализовать транзакцию, я получаю TransactionException с кодом 0x8004d00e.
Я пытаюсь сделать то, что описано в TransactionScope для доменов приложений и процессов и http://blogs.microsoft.co.il/blogs/sasha/archive/2010/04/30/propagating-a-transaction-across-appdomains.aspx. Тем не менее, не повезло.
Вот некоторые вещи, которые я пробовал, но безуспешно:
- Создание DependentTransaction через DependentClone () в дочернем процессе из загруженной транзакции
- Создание DependentTransaction через DependentClone () в родительском процессе перед сохранением транзакции в файл
- Создание Clone () в родительском процессе перед сохранением транзакции в файл
- Сохранение транзакции с использованием сериализации
- Сохранение транзакции с помощью TransactionInterop.GetTransactionFromTransmitterPropagationToken ()
- Явное открытие соединения до родительского TransactionScope
- Явное присоединение транзакции к родительскому элементу
- Явное привлечение транзакции внутри дочернего элемента
- Завершение / не завершение области родительского
- Завершение / не завершение области для ребенка
- Явное создание CommittableTransaction в родительском элементе
Вот одно сообщение об исключении:
System.Transactions.TransactionException: транзакция уже неявно или явно зафиксирована или прервана. ---> System.Runtime.InteropServices.COMException: транзакция уже была явно или неявно зафиксирована или прервана (исключение из HRESULT: 0x8004D00E)
И другой (при использовании DependentClone ()):
System.Transactions.TransactionAbortedException: транзакция прервана. в System.Transactions.TransactionStatePromotedAborted.CreateBlockingClone (In ternalTransaction tx) в System.Transactions.DependentTransaction..ctor (IsolationLevel isoLevel, In ternalTransaction internalTransaction, логическая блокировка) в System.Transaction.Transaction.DeCloud
Есть идеи, что я делаю не так? Я пробовал много его перестановок, но безуспешно.
Вот код (который не пытается продемонстрировать все варианты, описанные выше):
// one variant I have tried is to create a CommittableTransaction
// and pass that in the scope below
using (TransactionScope scope = new TransactionScope())
{
// optionally, do some parent-level EF work
// invoke child operations in other processes
DoChildOperation_OutOfProc(1, Transaction.Current);
DoChildOperation_OutOfProc(2, Transaction.Current);
scope.Complete();
}
// in the variant where I created a CommittableTransaction,
// I committed it here
...
private static void DoChildOperation_OutOfProc(int id, Transaction transaction)
{
string tranFile = string.Format("ChildTran_{0}.txt", id);
SaveTransactionToFile(transaction, tranFile);
Process process = new Process();
process.StartInfo = new ProcessStartInfo(Process.GetCurrentProcess().MainModule.FileName.Replace(".vshost", string.Empty),
string.Format("-CHILDID={0} -TRANFILE={1}", id, tranFile));
process.StartInfo.UseShellExecute = false;
process.Start();
process.WaitForExit();
}
private static void SaveTransactionToFile(Transaction transaction, string tranFile)
{
byte[] transactionBytes =
TransactionInterop.GetTransmitterPropagationToken(transaction);
string tranFileContents = Convert.ToBase64String(transactionBytes);
File.WriteAllText(tranFile, tranFileContents);
}
private static Transaction LoadTransactionFromFile(string tranFile)
{
string tranFileContents = File.ReadAllText(tranFile);
File.Delete(tranFile);
byte[] tranBytes = Convert.FromBase64String(tranFileContents);
Transaction tran =
TransactionInterop.GetTransactionFromTransmitterPropagationToken(tranBytes);
return tran;
}
// the child instance of the app runs this after decoding the arguments
// from DoChildOperation_OutOfProc() and loading the transaction out of the file
private static void DoChildOperation(int id, Transaction childTransaction)
{
// in one variant, I call
// childTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete)
// and then use that inside the TransactionScope
using (TransactionScope scope = new TransactionScope(childTransaction))
{
// do EF work and call SaveChanges()
scope.Complete();
}
// if I created a dependent clone, call Complete() here on it