使用 MVC 和 Fluent Nhibernate,在将 ViewModel 上的唯一字段绑定到域对象并保存它们之前,如何验证它们?
我有一个网站,允许用户创建新的零件记录。我正在尝试找出验证特定字段唯一性的最佳方法。我想确保如果零件编号已存在于其他零件上,则不会有人尝试添加零件编号为 1234 的零件。
Web 应用程序使用 Asp.net MVC 和流畅的 nHibernate 将我的对象映射到数据库。我在我的视图模型上使用 Castle 验证,例如 ValidateNonEmpty、ValidateRange 等。我应该使用 ValidateSelf 方法来查询存储库以查看该零件号是否已存在?在 ViewModel 上使用我的存储库感觉有些不对劲。
我是否将该逻辑放在控制器操作上会更好?这似乎不对,因为我希望我的 ViewModel 已经在此时(在 ModelBind 期间)经过验证。
或者也许以上都不是。感谢您对此的任何帮助。
更新 好的,不确定这是否有帮助,但这是我的项目中典型创建操作的“保存”操作的样子:
public ActionResult Create(PartViewModel viewModel)
{
//I think I'd like to know if its Valid by this point, not on _repository.Save
if(ModelState.IsValid)
{
try
{
var part = _partCreateViewModelMap.MapToEntity(viewModel);
_repository.Save(part);
return Redirect("~/Part/Details/" + part.Id);
}
catch (Exception e)
{
// skip on down...
}
}
// return view to edit
return View(viewModel);
}
I have a website where I allow users to create new Part records. I'm trying to figure out the best way to validate specific fields for uniqueness. I want to make sure that somebody doesn't try to add a Part with PartNumber 1234 if that PartNumber already exists on a different Part.
The Web Application is using Asp.net MVC with fluent nHibernate for mapping my objects to the database. I'm using Castle validation on my view models for things like ValidateNonEmpty, ValidateRange, etc. Should I use the ValidateSelf method to query the repository to see if that part number already exists? Something doesn't feel right about using my Repository on the ViewModel.
Would it be better for me to place that logic on the controller action? That doesn't seem right because I expect my ViewModel to already be Validated at the point (during ModelBind).
Or maybe its none of the above. Thanks for any help on this one.
UPDATE
Ok, not sure if this will help, but here is what my Save action looks like for a typical Create Action in my project:
public ActionResult Create(PartViewModel viewModel)
{
//I think I'd like to know if its Valid by this point, not on _repository.Save
if(ModelState.IsValid)
{
try
{
var part = _partCreateViewModelMap.MapToEntity(viewModel);
_repository.Save(part);
return Redirect("~/Part/Details/" + part.Id);
}
catch (Exception e)
{
// skip on down...
}
}
// return view to edit
return View(viewModel);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我已经被问过很多次这个问题了。我的朋友担心他们是否可以从验证器代码执行数据访问。答案很简单。如果你需要这样做,你应该这样做。通常我们需要在每个抽象级别进行此类检查。在完成所有检查之后,您应该准备好捕获由于违反唯一约束而引起的异常。
I have been asked this question many times. My friends were worried about whether they can perform data access from the validator code. The answer is simple. If you need to do this, you should do it. Usually we need to do such checks at each level of abstraction. And after all checks you should be ready to catch an exception, caused by unique constraint violation.
如果您在数据库中定义了唯一约束,那么为什么不委托检查数据库是否已存在唯一值的责任呢?通过 NHibernate,您可以使用 NHibernate.Exceptions.ISQLExceptionConverter 接口来捕获和转换与约束违规相关的已知错误。您还可以使用 NHibernate.Exceptions.IViolatedConstraintNameExtracter 实现器(请参阅 NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter )来获取数据库异常的肮脏详细信息,并将其转换为用户异常友好的消息,重新打包为您选择的验证异常并在相关控制器中捕获它。
来自我的项目之一的快速、非常具体的快速和肮脏的异常转换器的示例:
通过
web.config/nhibernate-configuration/session-factory
属性元素进行配置:编辑: 应该提到的是,最近版本的 NHibernate 中的转换器接口已发生变化,此示例中的接口来自 NHibernate.dll v2.1.0.4000
If you define a unique constraint within the database, then why not delegate the responsibility to check for whether a unique value already exists to the database? Using NHibernate, you can use the
NHibernate.Exceptions.ISQLExceptionConverter
interface to capture and transform known errors relating to constraint violations. You can also useNHibernate.Exceptions.IViolatedConstraintNameExtracter
implementers (seeNHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter
) to get at the grubby details of your database exception, and transform it into a user-friendly message, repackage as a validation exception of your chosing and catch it in the relevant controller.Example of a quick, very specific quick and dirty exception converter from one of my projects:
Configured through the
web.config/nhibernate-configuration/session-factory
property element:Edit: Should probably mention that the converter interface has changed in recent versions of NHibernate, the interface from this example is from NHibernate.dll v2.1.0.4000
我通常在控制器和存储库之间放置一个服务层。
然后,服务层将处理验证和对存储库的调用。
然后,如果服务层中存在验证错误,我会抛出一个自定义异常,在控制器中捕获它,并将错误注入到模型状态中。
I typically put a Service layer between my controllers and repositories.
The service layer would then handle the validation and calls to the repository.
Then, if there's a validation error in the service layer, I throw a custom exception, catch it in the controller, and inject the errors in to the model state.
我没有回答你的问题,但你可以查看 Sharparchitecture.net 网站。它包含 asp.net mvc 和 nhibernate 的一些最佳实践。我还可以建议您检查 xval 项目和有关使用数据注释验证器进行验证的教程
I have no answer for your question but you can check sharparchitecture.net site. It contains some best practives for asp.net mvc and nhibernate. Also I can recommend you to check xval project and tutorials about Validating with Data Annotation Validators
我发现适合我的解决方案是
1.) 询问实体是否有效来执行您的验证工作。
2.) 完成此操作后,您的对象上应该有一些东西来显示它是否有效(在我的例子中,我使用类似 CSLA 的“破坏规则”概念)。
3.) 如果您有类似的情况,您可以在 NHibernate 尝试持久化该对象之前验证该对象是否有效,如下所示。
这种方法的唯一问题是您确实需要在每个需要验证的实体上实现一个接口。如果您可以忍受这一点,它将阻止 NHibernate 保留根据您的规则无效的对象的更改。
I have found the solution that works for me is to
1.) Ask if the entity is valid to execute your validation work.
2.) After this is complete you should have something on your object to show it's valid or not (in my case I use a CSLA like concept of "broken rules").
3.) If you have something like this you can verify the object is valid before NHibernate tries to persist it as shown below.
The only issue with this approach is that you do need to implement an interface on each entity requiring validation. If you can live with this it will stop NHibernate from persisting the changes of an object that's not valid according to your rules.
我想说这对你的架构很重要。在我过去完成的 MVC 应用程序中,我们将领域内容从 Web 内容中抽象出来,自然地我们使用依赖注入来避免硬依赖。
当您在绑定模型时验证模型时,您可以轻松地在 ValidateSelf 方法中使用服务、存储库或体系结构中接下来的任何内容。我认为问题在于这种依赖性如何。
如果我没记错的话,您可以创建自己的自定义绑定器,它将使用依赖项注入框架来插入模型在创建时验证所需的任何服务,调用 MVC 的默认绑定器来填充对象,然后调用 Castle Validation 的框架进行验证。这不是一个经过深思熟虑的解决方案,但希望它能激发一些想法。
I would say this matters on your architecture. With MVC apps that I have done in the past we abstract away the domain stuff away from the web stuff and naturally we use dependency injection to avoid hard dependencies.
When it comes to validating the model when you are in the act of binding it, yes you could easily use the service, repository, or whatever you have next in your architecture in a ValidateSelf method. I think the question rises of what about that dependency.
If I remember correctly you can create your own custom binder that will use your dependency injection framework to plug-in any services your model needs for validation when you create it, call MVC's default binder to fill in the object, then call into Castle Validation's framework to do the validation. This isn't a fully thought solution, but hopefully it provokes some ideas.