我们使用存储过程在数据库中实现大多数业务规则.
我永远无法决定如何最好地将数据约束违规错误从数据库传递回用户界面.我所谈论的约束更多地依赖于业务规则而不是数据完整性.
例如,诸如"无法插入重复键行"之类的db错误与业务规则"您不能具有多个具有相同名称的Foo"相同.但是我们已经在最常识的位置"实现"了它:作为一个唯一的约束,当规则被违反时抛出异常.
其他规则,例如"你每天只允许100个Foos"不会导致错误,因为它们由自定义代码优雅地处理,例如return empty dataset
应用程序代码检查并传递回ui层.
这就是问题所在.我们的ui代码看起来像这样(这是AJAX.NET webservices代码,但任何ajax框架都可以):
WebService.AddFoo("foo", onComplete, onError); // ajax call to web service function onComplete(newFooId) { if(!newFooId) { alert('You reached your max number of Foos for the day') return } // update ui as normal here } function onError(e) { if(e.get_message().indexOf('duplicate key')) { alert('A Foo with that name already exists'); return; } // REAL error handling code here }
(作为旁注:我注意到这是stackoverflow在您提交评论过快时所执行的操作:服务器生成HTTP 500
响应并且ui捕获它.)
所以你看,我们在这里处理两个地方的业务规则违规,其中一个(即唯一的constaint错误)作为特殊情况处理,应该处理真正的错误(而不是违反业务规则),因为.NET将Exceptions一直传播到onError()
处理程序.
这感觉不对.我认为我的选择是:
捕获应用程序服务器级别的"重复密钥冲突"异常并将其转换为ui期望的"违反业务规则"标志的任何内容,
抢占错误(例如,使用a "select name from Foo where name = @Name"
)并返回app服务器期望的"违反业务规则"标志,
与2)相同的球场:利用内置于数据库层的唯一约束,盲目地insert into Foo
捕获任何异常并将其转换为应用服务器期望的"业务规则违反"标志
盲目地insert into Foo
(如3)并让Exception传播到ui,并让app服务器将业务规则违规提升为真实Exceptions
(而不是1).这样,所有错误都在ui层onError()
(或类似)代码中处理.
我喜欢的2)和3)是业务规则违规被"抛出" 它们被实现的地方:在存储过程中.我不喜欢1)和3)我认为它们涉及愚蠢的检查"if error.IndexOf('duplicate key')"
,就像目前ui层中的内容一样.
编辑:我喜欢4),但大多数人都说Exception
只在特殊情况下使用s .
那么,你们如何优雅地处理传播业务规则违规行为?
我们不在数据库中执行业务逻辑,但我们确实拥有所有验证服务器端,低级DB CRUD操作与更高级别的业务逻辑和控制器代码分开.
我们在内部尝试做的是传递具有类似函数的验证对象Validation.addError(message,[fieldname])
.各种应用程序层将其验证结果附加到此对象上,然后我们调用Validation.toJson()
以生成如下所示的结果:
{ success:false, general_message:"You have reached your max number of Foos for the day", errors:{ last_name:"This field is required", mrn:"Either SSN or MRN must be entered", zipcode:"996852 is not in Bernalillo county. Only Bernalillo residents are eligible" } }
这可以很容易地在客户端处理,以显示与各个字段以及一般消息相关的消息.
关于约束违规,我们使用#2,即我们在插入/更新之前检查潜在的违规,并将错误附加到验证对象.