使用客户端代码(JavaScript、Angular 等)时,您必须从 HTTP 协议的角度来看待它。单个 HTTP 请求应被视为一个原子操作(=事务)。
其中一个原因是,HTTP 是一种无状态协议,默认情况下不会保留状态,虽然您可以解决它(会话),但拥有状态(保存在会话中的值,临时数据等)。 are request 所需的一切都应随 request 一起传递。
此外,像 EntityFramework Core 这样的 ORM 的默认生命周期是“范围内的”,这意味着它在请求开始时创建并在请求结束时释放。这对于 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/cart 或 POST /api/cart/{position:int})
- 用户将另一件商品放入购物车
- 用户结帐并被要求添加送货地址和付款方式
- 如果不存在,用户创建一个帐户(调用 POST /api/register)
- 用户获得概览并必须确认(POST /api/cart/processOrder)
每个操作都将对象放入一个容器(此处为购物车)中,最后您处理它并从那里获取它并创建订单。
如果订单是由公司的员工创建的(即客户通过电话订购)并且流程不同,则此方法的工作方式会有所不同。
那里没有购物车。不过处理步骤类似。
-
员工打开一个新订单 (POST /api/orders) 并可能收到这样的 JSON 响应(可能包含状态和填充的一些内容,例如当前日期、部门和打开它的员工 ID)
{
"orderId": "unique-guid",
"createdOn": "2017-01-07T15:01:24",
"createdBy": "employee-guid-here",
"state": "open",
"orderNum": null,
"orderDetails": []
}
这是向员工显示的。
- 员工从客户那里接过订单并不断向其中添加新的订单详细信息,每个订单都会触发对 API 的请求 (POST /api/order/unique-guid)
- 完成后,员工向客户询问其客户编号。他检查它的存在 (GET /api/customer/{id})。如果没有找到,他会向客户询问详细信息并发布结果(POST /api/customer)
- 最后,他要求客户确认他的订单并点击“下订单”按钮 (PUT /api/order/unique-guid) 并在成功处理后创建一个 orderNum。流程从打开变为“已放置”,并设置了“orderPlacedOn”字段
- 如果客户决定不订购,员工点击取消并注明原因“客户改变主意”或“价格太高”,订单将被标记为“已取消”或“关闭”
在这种情况下,订单仍保留在数据库中,但您有额外的业务价值。您可以查看所有下达的订单以及取消订单的原因(可用于在未来提高客户满意度并对其做出反应)以及跟踪员工在此订单上使用的时间(无投资回报率)(对于通过对取消订单的原因做出反应来提高获取率)。
在这种情况下,您完全无需交易即可工作并获得额外的商业价值。此外,如果浏览器崩溃或员工意外按 F5,则不会丢失任何内容(可能是上述单一事务方法的情况)。