Убедитесь, что вызов API успеха и сохранить себе успех - PullRequest
0 голосов
/ 24 января 2019

Здравствуйте. Я обнаружил проблему, когда использую ASP.NET MVC с EF и вызываю веб-API с другого веб-сайта (на котором также используется Entity Framework). проблема в том, что

Я хочу убедиться, что оба MVC SaveChanges() и веб-API SaveChanges() успешно работают вместе.

Вот псевдокод моей мечты

public ActionResult Operation()
{
    Code Insert Update Delete....

    bool testMvcSaveSuccess = db.TempSaveChanges();  //it does not have this command.

    if(testMvcSaveSuccess == true)
    {
       bool isApiSuccess = CallApi(); //insert data to Other Web App


       if(isApiSuccess == true)
       {
          db.SaveChanges(); //Real Save
       }
    }
}

Из приведенного выше кода, если он не имеет db.TempSaveChanges(), возможно, Web API будет успешным, но MVC SaveChanges() может завершиться ошибкой.

Ответы [ 4 ]

0 голосов
/ 24 января 2019

хорошо, давайте немного проясним ситуацию.

у вас есть приложение MVC A1 с собственной базой данных D1 у вас есть API, давайте назовем его A2 с собственной базой данных D2 .

вам нужен код в A1 , который сохраняет временные значения в D1 , затем запускает вызов A2 и, если ответ успешен, он сохраняет временные данные из D1 в нужном месте на этот раз.

на основе вашего псевдокод, я бы предложил вам создать вторую таблицу, где вы сохраните свои «временные» данные в D1 .Таким образом, в вашей базе данных есть дополнительная таблица, и поток выглядит следующим образом:

сначала вы сохраняете данные A1 в этой таблице, затем вызываете A2 , данные сохраняютсяв D2 , A1 получает подтверждение и вызывает метод, который перемещает данные из второй таблицы в нужное место.

Сценарии для рассмотрения:

  1. Сохранение временных данных в D1 работает, но вызов A2 завершается неудачно.Теперь вы удаляете потерянные данные с помощью пакетного задания или просто вызываете что-то, что удаляет их, когда сбой при вызове A2 .

  2. Вызов A2 завершается успешно, и вызов D1 завершается неудачно, поэтому теперь у вас есть временные данные в D1 , которые не удалось переместить в правую таблицу.Вы можете добавить флаг ко второй таблице напротив каждой строки, который указывает, что второй вызов A2 завершился успешно, поэтому эти данные должны перемещаться в нужном месте, когда это возможно.У вас может быть служба, которая периодически запускается, и если она находит какие-либо данные с установленным в true флагом, то она перемещает данные в нужное место.

Существуют и другие способы обработкисценарии, подобные этому.Вы можете использовать систему очередей для управления этим.Каждая строка данных становится сообщением, вы назначаете ему уникальный идентификатор, GUID, который в основном является CorrelationID, и он одинаков в обеих системах.Даже если одна система выйдет из строя, когда она вернется, данные будут сохранены, и в мире все хорошо, и благодаря общему идентификатору вы всегда можете правильно связать их.

0 голосов
/ 24 января 2019

Так что нет ничего похожего на TempSaveChanges, потому что есть что-то еще лучше: Транзакции .

Транзакция является IDisposable (может использоваться в блоке using) и имеет такие методы, как Commit и Rollback .

Небольшой пример:

private void TestTransaction()
{
    var context = new MyContext(connectionString);

    using (var transaction = context.Database.BeginTransaction())
    {
        // do CRUD stuff here 

        // here is your 'TempSaveChanges' execution
        int changesCount = context.SaveChanges();

        if (changesCount > 0)
        // changes were made
        {
            // this will do the real db changes
            transaction.Commit();
        }
        else
        {
            // no changes detected -> so do nothing
            // could use 'transaction.Rollback();' since there are no changes, this should not be necessary
            // using block will dispose transaction and with it all changes as well
        }
    }
}

Я извлек этот пример из моего GitHub Exercise.EntityFramework хранилища. Не стесняйтесь Star / Clone / Fork ...

0 голосов
/ 24 января 2019

То, на что вы ссылаетесь, известно как атомарность : вы хотите, чтобы несколько операций либо все были успешными, либо ни одной из них. В контексте базы данных вы получаете это через транзакции (если база данных поддерживает это). В вашем случае, однако, вам нужна транзакция, которая охватывает две непересекающиеся системы. Универсальная (в некоторых особых случаях есть более простые решения) надежная реализация такой транзакции будет иметь определенные требования к двум системам, а также потребует дополнительного сохранения.

По сути, вы должны быть в состоянии изящно восстановиться после внезапной остановки в любой точке последовательности. Каждая из используемых вами баз данных, скорее всего, совместима с ACID , поэтому вы можете рассчитывать на каждую транзакцию БД для выполнения требования атомарности (они либо успешны, либо нет). Поэтому все, что вам нужно беспокоиться, это последовательность двух транзакций БД. Ваше требование к двум системам - это способ определить апостериорно, была ли выполнена какая-либо операция.

Пример технологического процесса:

  1. Операция начинается
  2. Создать уникальный идентификатор транзакции и сохранить (с данными запроса)
  3. Внести изменения в локальную БД и зафиксировать
  4. Вызов внешнего веб-API
  5. Пометить транзакцию как завершенную (или удалить)
  6. Операция заканчивается

Восстановление:

  1. Получить все ожидающие (не завершенные) транзакции из магазина
  2. Проверить, было ли произведено ожидаемое изменение в локальной БД
  3. Запросить Web API, если ожидаемое изменение было сделано
  4. Если ни одно из изменений не было выполнено или оба изменения были сделаны, то транзакция выполнена: удалите / отметьте ее.
  5. Если было внесено одно из изменений, а не другое, то либо отменить внесенное изменение (отменить транзакцию), либо выполнить изменение, которого не было (возобновить транзакцию) =>, затем удалить / пометить его.

Теперь, как вы можете видеть, это быстро усложняется, особенно если «определение внесения изменений» является нетривиальной операцией. Для этого обычно используется уникальный идентификатор транзакции в качестве средства определения того, какие данные требуют внимания. Но в этот момент он становится очень специфичным для приложения и полностью зависит от конкретных операций. Для некоторых приложений вы можете просто повторно выполнить всю операцию (так как все данные запроса хранятся в транзакции) на этапе восстановления. В некоторых особых случаях нет необходимости сохранять транзакцию, поскольку существуют другие способы достижения тех же целей и т. Д.

0 голосов
/ 24 января 2019

Да, вы можете.

вам нужно перегрузить .Savechanges в классе контекста, где он будет вызываться, сначала проверяется, а затем вызывать регулярное после.

Или создайте свой собственный TempSaveChanges() в классе контекста, затем вызовите его, если вызов успешно SaveChanges из него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...