Подтвердить что ты не робот

Игнорировать 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, и я бы скорее просто справился с этим в одном месте.

4b9b3361

Ответ 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;
    }
}