1
+==========+  +==========+  +=========+  +======+ 
+ Angular +<---->+ ASP.NET +<---->+ Service +<---->+ EF +<---->(Database) 
+ Client +  + Core API +  + Layer +  + Core + 
+==========+  +==========+  +=========+  +======+ 

Вопрос: где вы реализуете транзакции базы данных в архитектуре AngularJS/.NET Core? (Диаграмма выше иллюстрирует возможную архитектуру.)Операции с базами данных в архитектуре ядра AngularJS/.NET

Пример сценария - в одной транзакции с базой данных вы хотите (1) вставить одну новую запись клиента, (2) вставить одну новую запись заказа и (3) вставить две новые подробные записи заказов.

+0

Вы должны быть более конкретным, какой слой выполняет изменения в базе данных? Согласно вашей информации, я думаю, что это в EF Core –

+0

. Я буду использовать пример. Ниже приведен поток процессов для вставки записи клиента: (1) Угловой клиент выполняет HTTP POST для контроллера клиента (в API-интерфейсе ASP.NET), (2) контроллер клиента вызывает уровень обслуживания, который, в свою очередь, вызывает методы EF Core для добавления и фиксации записи клиента в базу данных. Нет необходимости в явной транзакции, поскольку есть только одна запись (а вызов EF SaveChanges() обертывает вставку в неявной транзакции). – stickian

+0

Итак, проблема возникает, когда я хочу, например, использовать одну транзакцию для вставки нескольких записей в несколько таблиц. Куда должен идти код, который управляет транзакцией, которая вставляет несколько записей? – stickian

ответ

2

При работе с клиентским кодом (JavaScript, Angular и т. Д.) Вы должны смотреть на него с точки зрения протокола HTTP. Один HTTP-запрос следует рассматривать как одну атомную операцию (= транзакция).

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

Также по умолчанию для ORM, таких как EntityFramework Core, используется термин «облачно», что означает, что он создается, когда запрос начинается и удаляется, когда запрос заканчивается. Это важно для операций ACID/транзакций и управления памятью (иначе объект будет отслеживаться навсегда и вызвать утечку памяти). Таким образом, вы не должны вмешиваться или пытаться обойти это ограничение.

Если вы хотите, чтобы несколько операций с клиентской стороны (браузера) обрабатывались как одна операция, вам необходимо собрать их вместе в структуру, а затем отправить их с помощью одного запроса в базу данных.

Однако это может быть немного сложно сделать, оставаясь верным принципам обслуживания RESTful.

Одним из способов является создание специализированного ViewModel, который будет сфера сделки, давайте называть его OrdersTransactionScopeViewModel и вместо того, чтобы создавать одиночные Order и OrderDetail модели вы создаете одну OrderViewModel модель, которая содержит как.

public class OrdersTransactionScopeViewModel 
{ 
    public Customer Customer { get; set; } 
    public List<OrderViewModel> Orders { get; } = new List<OrderDetailViewModel>(); 
} 

public class OrderViewModel 
{ 
    public List<OrderDetailViewModel> { get; } = new List<OrderDetailViewModel>(); 
    public ShippingMethod ShippingMethod { get; set; } 
} 

public class OrderDetailViewModel 
{ 
    public int OrderPosition { get; set; } 
    public string PartNumber { get; set; } 
    public decimal Quantity { get; set; } 
} 

Затем соберите все это на стороне клиента и отправьте их сразу.

{ 
    "customer": { "name": "Firstname", "lastname": "Lastname", "address": { ...}, 
    "order": [{ 
     orderDetails: [{ 
      "orderPosition": 1, 
      "partNumber": "12345", 
      "quantity": 2 
     },{ 
      "orderPosition": 2, 
      "partNumber": "11111", 
      "quantity": 1 
     },{ 
      "orderPosition": 1, 
      "partNumber": "666", 
      "quantity": 12 
     }] 
    }] 
} 

Затем у вас есть один объект, отправленный службе REST, и вы можете обрабатывать его как одну транзакцию. Прочтите свойства, создайте модели персистентности и вставьте их.

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

Другой способ приблизиться к нему - это не рассматривать его как атомную операцию.

Например:

  • Пользователь помещает элемент в корзину (вызов POST/API/корзина или POST/API/корзина/{положение: Int}, если вы хотите быть идемпотентна)
  • пользователь помещает еще один элемент в корзине
  • пользователь проверяет и просят, чтобы добавить адрес доставки и способ оплаты
  • пользователь создает учетную запись (вызов POST/API/регистр), если не существует
  • Пользователь получает (POST/api/cart/processOrder)

Каждая из операций помещает объекты в контейнер (здесь корзина для покупок), а в конце вы обрабатываете его и получаете оттуда и создаете приказ.

Это работает несколько иначе, если заказ создается сотрудником компании (т. Е. Заказы клиентов по телефону), и процесс отличается.

Там у вас нет корзины покупок. Тем не менее этапы обработки аналогичны.

  • Работника и открывает новый порядок (POST/API/заказы) и может получить ответ в формате JSON (который может содержать состояние и несколько вещей, которые заселены как текущую дату, деление и идентификатор сотрудника, который открыл его) например

    { 
        "orderId": "unique-guid", 
        "createdOn": "2017-01-07T15:01:24", 
        "createdBy": "employee-guid-here", 
        "state": "open", 
        "orderNum": null, 
        "orderDetails": [] 
    } 
    

    Это отображает его на сотрудника.

  • Работник принимает заказ от клиента и продолжает добавлять новые детали заказа к нему, каждому запуская запрос в API (POST/API/заказ/уникальный-справ)
  • После завершения, сотрудник просит клиент для его номер клиента. Он проверяет его существование (GET/api/customer/{id}). Если он не найден, он запрашивает у клиента детали и результат отправляется (POST/api/customer)
  • Наконец, он просит клиента подтвердить свой заказ и нажать кнопку «заказ места» (PUT/api/order/unique-guid) и orderNum создается после его успешной обработки. Процесс изменяется от открытого к «помещенному» и устанавливается поле «orderPlacedOn».
  • Если клиент решает не заказывать его, сотрудник ударяет по аннулированию и отмечает, что причина «Клиент передумал» или «Цены слишком высокие» и заказ будет помечен как «отменен» или «закрыт»

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

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

0

Последующая вещь может быть вашей помощью.

  1. Прежде всего, я хотел бы остановиться на том, что если вы создаете приложение для реального проекта, вам не хватает одного важного слоя: уровень репозитория. Это происходит между уровнем обслуживания и уровнем EF.

  2. Вы можете рассматривать EF-слой как единицу работы, но в этом случае вам нужно выставить его на Service-слой, поэтому было бы неплохо создать концепцию единицы работы.

  3. Затем на уровне обслуживания вы можете использовать репозиторий вместе с Единицей работы. Здесь Единица работы инициирует транзакцию между другими репозиториями.

http://www.aspnetboilerplate.com/Pages/Documents/Unit-Of-Work

Вы можете найти больше пример онлайн.

+0

Проблема здесь не реализуется на стороне сервера, но приложение работает на клиенте (браузер, JavaScript) и что несколько независимых запросов должны быть связаны с одной транзакцией. Это не очень хорошо работает из-за ограниченной степени DbContext (и Singleton не имеет смысла с точки зрения ресурсов и производительности). Таким образом, невозможно спамить одну транзакцию, позволяя сказать 5 запросов HTTP (создать клиента, создать заказ, создать 3 детали) – Tseng