Gar*_*rez 12 entity-framework odata asp.net-web-api
使用Web API OData,我有$ batch处理工作,但是,数据库的持久性不是Transactional.如果我在请求中的Changeset中包含多个请求,并且其中一个项失败,则另一个仍然完成,因为对控制器的每个单独调用都有它自己的DbContext.
例如,如果我提交一个包含两个更改集的批处理:
批处理1 - 更改集1 - - 修补程序有效对象 - - 修补程序无效对象 - 结束更改集1 - 更改集2 - - 插入有效对象 - 结束更改集2结束批处理
我希望第一个有效补丁将被回滚,因为更改集无法完整地完成,但是,因为每个调用都有自己的DbContext,第一个补丁被提交,第二个补丁没有,插入是承诺.
有没有一种标准的方法来通过OData的$ batch请求来支持事务?
tne*_*tne 14
为了记录,这是OData规范有关它的说法(强调我的):
更改集中的所有操作都代表单个更改单元,因此服务必须成功处理并应用更改集中的所有请求,否则不应用任何更改集.由服务实现来定义回滚语义以撤消在同一更改集中的另一个请求失败之前可能已应用的更改集内的任何请求,从而应用此全有或全无要求.服务可以按任何顺序在变更集内执行请求,并且可以按任何顺序将响应返回给各个请求.(......)
这是V4,它几乎不会更新有关批量请求的V3,因此相同的注意事项适用于V3服务AFAIK.
要理解这一点,您需要一些背景知识:
你可能会对变更集中的请求是无序的这一事实表示怀疑,坦率地说,我没有适当的理由提供.规范中的示例清楚地显示了相互引用的请求,这意味着必须推导出处理它们的顺序.实际上,我的猜测是变更集必须真正被认为是单个请求本身(因此是原子需求),它们被一起解析并且可能被折叠成单个后端操作(当然取决于后端).在大多数SQL数据库中,我们可以合理地启动事务并按照它们的相互依赖性定义的特定顺序应用每个子请求,但是对于其他一些后端,可能需要在将任何更改发送到之前对这些子请求进行修改并一起理解.盘片.这可以解释为什么不要求它们按顺序应用(这个概念可能对某些后端没有意义).
这种解释的含义是,所有变更集必须在逻辑上一致; 例如,您不能在同一个更改集上触摸相同属性的PUT和PATCH.这将是模棱两可的.因此,客户端有责任在将请求发送到服务器之前尽可能高效地合并操作.这应该总是可行的.
(我希望有人能证实这一点.)我现在相信这是正确的解释.
虽然这似乎是一个明显的好习惯,但人们通常不会想到批量处理.我再次强调,所有这些都适用于变更集中的请求,而不是批处理请求中的请求和变更集(这些请求是按照您的预期排序和工作,减去非原子/非事务性质).
回到你的问题,这是特定于ASP.NET Web API,似乎他们声称完全支持 OData批处理请求.更多信息在这里.正如你所说的那样,每个子请求都会创建一个新的控制器实例(好吧,我接受你的话),这反过来又会带来一个新的上下文并打破原子性要求.那么,谁是对的?
好吧,正如你正确指出的那样,如果你打算SaveChanges在你的处理程序中打电话,那么任何框架hackery都不会有多大帮助.看起来你应该自己处理这些子请求,并考虑我上面提到的注意事项(寻找不一致的更改集).很明显,您需要(1)检测到您正在处理属于变更集的子请求(以便您可以有条件地提交)和(2)在调用之间保持状态.
更新:在保持控制器不知道功能的情况下(不需要(1)),请参阅下一节有关如何执行(2)的内容. 如果您想要解决HttpMessageHandler方案所解决的问题的更多背景,那么接下来的两段可能仍然会引起关注.
我不知道您是否可以使用他们提供的当前API来检测您是否处于变更集中(1).我不知道你是否可以强制ASP.NET使控制器保持活动状态(2).你可以为后者做些什么(如果你不能保持活着)是在其他地方保持对上下文的引用(例如在某种会话状态中 Request.Properties)并有条件地重用它(更新:或无条件地,如果你管理更高层次的交易,见下文).我意识到这可能没有你想象的那么有用,但至少现在你应该有正确的问题指向你的实现的开发人员/文档编写者.
危险地漫无目的:SaveChanges您可以有条件地创建和终止TransactionScope每个变更集,而不是有条件地调用.这不会消除对(1)或(2)的需要,只是另一种做事方式.接下来,框架可以在技术上自动实现它(只要可以重用相同的控制器实例),但是如果不知道内部结构,我就不会重新审视我的声明框架没有足够的内容现在就做好一切.毕竟,TransactionScope某些后端的语义可能过于具体,无关紧要甚至不受欢迎.
更新:这确实是正确的做事方式.下一节将显示使用Entity Framework显式事务API而不是的示例实现TransactionScope,但这具有相同的最终结果.虽然我觉得有很多方法可以实现通用的实体框架实现,但是目前ASP.NET并没有提供任何特定于EF的功能,所以你需要自己实现它.如果您提取代码以使其可重用,请尽可能在ASP.NET项目之外共享它(或说服ASP.NET团队将它们包含在树中).
请参阅snow_FFFFFF的有用答案,该答案引用了一个示例项目.
将它放在这个答案的上下文中,它展示了如何使用一个HttpMessageHandler来实现我在上面概述的需求#2(在单个请求中保持控制器的调用之间的状态).这通过挂钩比控制器更高级别,并在多个"子请求"中拆分请求,同时保持状态无视控制器(事务),甚至将状态暴露给控制器(实体框架上下文,在此案件通过HttpRequestMessage.Properties).控制器愉快地处理每个子请求,而不知道它们是正常请求,批处理请求的一部分,还是变更集的一部分.他们需要做的就是在请求的属性中使用Entity Framework上下文,而不是使用自己的.
请注意,您实际上有很多内置支持来实现此目的.这个实现建立在DefaultODataBatchHandler构建于ODataBatchHandler代码之上的代码之上,代码构建在代码之上HttpBatchHandler,这是一个代码HttpMessageHandler.使用相关请求显式路由到该处理程序Routes.MapODataServiceRoute.
这个实现如何映射到理论?很好,实际上.您可以看到每个子请求被发送以由相关控制器按原样处理,如果它是"操作"(正常请求),或者如果它是变更集则由更具体的代码处理.在这个级别,它们按顺序处理,但不是原子地处理.
但是,变更集处理代码确实将每个自己的子请求包装在一个事务中(每个变更集一个事务).虽然代码可以在此时尝试通过查看每个子请求的Content-ID头来构建依赖关系图来确定在事务中执行语句的顺序,但是这种实现采用了更直接的方法来要求客户端以正确的顺序对这些子请求进行排序,这是公平的.
如果您可以将所有操作包装在一个变更集中,那么请求将是事务性的.如果不能,则必须修改此实现,以便将整个批处理包装在单个事务中.虽然规范并未排除这一点,但需要考虑明显的性能因素.您还可以添加一个非标准HTTP标头来标记您是否希望批处理请求是事务性的,并使您的实现相应地执行.
在任何情况下,这都不是标准的,如果你想以可互操作的方式使用其他OData服务器,你就不能指望它.要解决这个问题,您需要争取向OASIS的OData委员会提出可选的原子批量请求.
如果在处理变更集时找不到分支代码的方法,或者您无法说服开发人员为您提供这样做的方法,或者您无法以任何令人满意的方式保持特定于变更集的状态,那么看起来你必须 [你可能会想要]公开一个全新的HTTP资源,其中包含特定于你需要执行的操作的语义.
您可能知道这一点,这可能是您要避免的,但这涉及使用DTO(数据传输对象)来填充请求中的数据.然后,您可以解释这些DTO以在单个处理程序控制器操作中操纵您的实体,从而完全控制结果操作的原子性.
请注意,有些人实际上更喜欢这种方法(更多面向流程,更少数据导向),尽管建模起来非常困难.没有正确的答案,它总是取决于域和用例,并且很容易陷入陷阱,使您的API不是非常RESTful.这是API设计的艺术.不相关:关于数据建模可以说同样的评论,有些人实际上发现更难.因人而异.
有一些方法可供探索,一些信息可以从开发人员那里获取一个规范的实现技术,一个创建通用实体框架实现的机会,以及一个非通用的替代方法.
如果你能在其他地方收集答案(好吧,如果你有足够的动力)以及你最终决定做的事情,那么你可以更新这个帖子会很好,因为看起来许多人都喜欢有某种明确的指导对于.
祝好运 ;).
以下链接显示了处理事务中的变更集所需的 Web API OData 实现。您是正确的,默认批处理程序不会为您执行此操作:
http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v3/ODataEFBatchSample/
更新 原始链接似乎已经消失 - 以下链接包含用于事务处理的类似逻辑(适用于 v4):
https://damienbod.com/2014/08/14/web-api-odata-v4-batching-part-10/
| 归档时间: |
|
| 查看次数: |
8452 次 |
| 最近记录: |