Игнорировать TransactionScope для конкретного запроса
Я ищу способ выполнить запрос, когда TransactionScope жив, и игнорировать TransactionScope - в основном, я хочу выполнить этот конкретный запрос независимо от того, что.
Я использую сначала код EF и способ разработки приложения, новый контекст данных открывается много раз в течение одного вызова, каждый со своими собственными изменениями, и все они содержатся в пределах одного TransactionScope, который имеет Complete()
, называемый в конце, не допуская ошибок. Внутри контекста мы переопределили SaveChanges
, чтобы, если какое-либо исключение происходит на base.SaveChanges()
, мы можем поймать его и зайти в базу данных, прежде чем откатывать транзакцию.
Поскольку SaveChanges
происходит внутри транзакции, ведение журнала, очевидно, не происходит, поскольку оно относится к той же транзакции, что и исходный вызов. Я пытаюсь вообще игнорировать TransactionScope только для кода ведения журнала.
Вот несколько усеченных кодов:
// From the context
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (Exception ex) {
// Writes to the log table - I want this to run no matter what
LogRepo.Log(/*stuff to log from the context*/);
throw;
}
}
// Inside the business logic
public void DoSomething() {
try {
using (var scope = new TransactionScope()) {
using (var context = new FooContext()) {
// Do something
context.SaveChanges();
}
using (var context = new FooContext()) {
// Do something else
context.SaveChanges();
}
scope.Complete();
}
} catch (Exception ex) {
// scope.Complete is never called, so the transaction is rolled back
}
}
Я попытался использовать обычный ADO.NET вместо EF для ведения журнала, но все же те же результаты - он тоже откатывается.
Мне нужна обработка ошибок внутри SaveChanges
, потому что то, что я регистрирую, - это состояние сохраняемых объектов, поэтому я не могу просто переместить запись в другое место. Я мог бы создать сообщение, находясь внутри SaveChanges catch
, и выбросить его, и пусть DoSomething catch
занесет его в журнал, но есть десятки методов DoSomething
, и я бы скорее просто справился с этим в одном месте.
Ответы
Ответ 1
Если вы завершаете свой лог-вызов внутри другой области транзакции с включенной опцией подавления, область транзакции не будет использоваться.
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (Exception ex) {
string message = /*stuff to log from the context*/;
using (var scope = new TransactionScope(TransactionScopeOption.Suppress))
{
LogRepo.Log(msg);
}
throw;
}
}
Ответ 2
Только моя первоначальная мысль, но вам нужно поставить свой LogRepo на свой собственный DataContext (DC2), чтобы окружающий TransactionScope (с DC1) не откатил его, когда он не был зафиксирован.
В принципе, вам необходимо сделать ваш журнал самодостаточным и атомарным.
EDIT. Посмотрев на это еще немного, мне кажется, что если вы переведете Logging из SaveChanges в catch() на DoSomething(), ваш журнал будет работать. Но ваш журнал все еще должен быть автономным и атомарным.
Ответ 3
Я нашел одно решение, которым я не очень доволен, но, похоже, работает. TransactionScope, по-видимому, влияет только на текущий поток, поэтому регистрация с использованием нового потока, похоже, работает нормально.
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (Exception ex) {
string message = /*stuff to log from the context*/;
new Thread(msg => {
LogRepo.Log(msg);
}).Start(message);
throw;
}
}