При работе с клиентским кодом (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, ничего не теряется (что может иметь место в случае подхода с одной транзакцией выше).
Вы должны быть более конкретным, какой слой выполняет изменения в базе данных? Согласно вашей информации, я думаю, что это в EF Core –
. Я буду использовать пример. Ниже приведен поток процессов для вставки записи клиента: (1) Угловой клиент выполняет HTTP POST для контроллера клиента (в API-интерфейсе ASP.NET), (2) контроллер клиента вызывает уровень обслуживания, который, в свою очередь, вызывает методы EF Core для добавления и фиксации записи клиента в базу данных. Нет необходимости в явной транзакции, поскольку есть только одна запись (а вызов EF SaveChanges() обертывает вставку в неявной транзакции). – stickian
Итак, проблема возникает, когда я хочу, например, использовать одну транзакцию для вставки нескольких записей в несколько таблиц. Куда должен идти код, который управляет транзакцией, которая вставляет несколько записей? – stickian