验证重用定制的ViewModels属性 [英] Reusing validation attributes in custom ViewModels

查看:126
本文介绍了验证重用定制的ViewModels属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我开始使用 XVAL 以客户端验证,我只是执行行动的方法使用该域模型对象作为一个视图模型或视图模型这些对象的嵌入式实例。

此方法工作得很好的大部分时间,但是当视图需要显示和当用户想要更新他的密码后回到只有模型的属性的子集(例如有这样的情况,但不其余他配置文件数据)。

一(丑陋的)解决办法是有形式不另外在表格上present每个属性上的隐藏输入字段。

显然这里最好的做法是创建一个自定义的视图模型只包含相关的视图属性和填充视图模型通过 Automapper 。这是干净多了,因为我只传送相关的数据视图,但它远非完美,因为我要重复那些已经美元域模型对象页上$ psent相同的验证属性。

在理想情况下,我想指定的域模型对象通过元数据属性元类(这也常常被称为伙计班),但是,这并不工作,因为XVAL抛出时元数据类有属性不美元的视图模型p $ psent。

有没有解决办法优雅这个?我一直在考虑的黑客攻击源XVAL code,但也许有是迄今为止我忽略了一些其他的方式。

谢谢,

阿德里安

编辑:使用ASP.NET MVC 2的到来,这不仅关系到验证属性的问题了,但它也适用于编辑和显示属性。


解决方案

这是典型的原因,你的输入屏幕不应该紧密耦合模型。这个问题实际上是对MVC标签这里会弹出一个每月约3-4倍。我欺骗,如果我能找到的previous问题和一些评论的讨论在这里很有趣。 ;)

您遇到的问题是你想迫使模型的两个不同的验证环境在其中大量的场景下失败的单一模式。最好的例子是注册一个新用户,然后有一个管理员后编辑用户领域。你需要验证在注册过程中,用户对象上的密码,但你不会密码字段展现给管理员编辑用户的详细信息。

为解决这些获得这些选项都是最佳的。现在我已经在这个问题上工作了3个项目,并实施了以下解决方案从来没有干净的,通常令人沮丧。我要去尝试,并实际并忘记所有的DDD / DB /模型/ hotnessofthemonth讨论别人都拥有。

1)多视图模型
 具有几乎相同违反了DRY主要的ViewModels但我觉得这种做法的成本非常低。通常违反干安培了维护成本但恕我直言这样做的成本是最低的,并不等于多少。假设说你不改变姓氏字段可数最多如何字符有非常频繁。

2)动态元
有在MVC 2挂钩为模型提供自己的元数据。通过这种方法,你可以拥有任何你使用提供元数据排除基于当前HTT prequest,因此行动和控制器的某些字段。我用这个技术来建立一个数据库驱动的权限系统,进入数据库,并告诉了DataAnnotationsMetadataProvider的一个子类,以排除基于存储在数据库中值的属性。

此技术是伟大的工作ATM但唯一的问题是与的UpdateModel验证()。为了解决这个问题,我们创建了一个 SmartUpdateModel()方法,也去到数据库,并自动生成排除String []数组,这样任何非permissisable字段不验​​证。我们当然这个缓存性能方面的原因所以它不坏。

只是想重申,我们使用[ValidationAttributes]在我们的模型,然后对运行新的规则superceeded他们。最终的结果是,如果用户没有权限访问它 [必需] User.LastName场不被验证。

3)疯狂的接口动态代理的事情
最后一个技术我想是使用接口的ViewModels。最终的结果是我不得不从接口继承像 IAdminEdit IUserRegistration User对象。 IAdminEdit和IUserRegistration将都包含执行的所有背景下特定的验证想与接口Password属性DataAnnotation属性。

这需要一些两轮牛车和更学术的运动比什么都重要。带2和3中的问题是,的UpdateModel和DataAnnotationsAttribute提供商需要进行定制,以被告知这一技术的。

我的最大的绊脚石是我根本没有想整个用户对象发送到视图,这样我结束了使用动态代理创建 IAdminEdit <的运行情况/ p>

现在我明白这是一个非常具体的XVAL问题,但所有的道路,这样导致内部的MVC元数据提供商定制动态验证。由于所有的元数据,东西是新的没有什么是干净的或简单的在这一点上做的。这项工作,你必须做定制MVC的验证行为并不难,但在一些需要深入的了解如何将所有的内部工作。

When I started using xVal for client-side validation, I was only implementing action methods which used domain model objects as a viewmodel or embedded instances of those objects in the viewmodel.

This approach works fine most of the time, but there are cases when the view needs to display and post back only a subset of the model's properties (for example when the user wants to update his password, but not the rest of his profile data).

One (ugly) workaround is to have a hidden input field on the form for each property that is not otherwise present on the form.

Apparently the best practice here is to create a custom viewmodel which only contains properties relevant to the view and populate the viewmodel via Automapper. It's much cleaner since I am only transferring the data relevant to the view, but it's far from perfect since I have to repeat the same validation attributes that are already present on the domain model object.

Ideally I'd like to specify the Domain Model object as a meta class via a MetaData attribute (this is also often referred to as "buddy class"), but that doesn't work since xVal throws when the metadata class has properties that are not present on the viewmodel.

Is there any elegant workaround to this? I've been considering hacking the xVal sourcecode, but perhaps there is some other way I have overlooked so far.

Thanks,

Adrian

Edit: With the arrival of ASP.NET MVC 2, this is not only a problem related to validation attributes anymore, but it also applies to editor and display attributes.

解决方案

This is the quintessential reason why your input screens should not be tightly coupled to your model. This question actually pops up here on the MVC tag about 3-4 times a month. I'd dupe if I could find the previous question and some of the comment discussion here is interesting. ;)

The issue your having is you're trying to force two different validation contexts of a model into a single model which fails under a large amount of scenarios. The best example is signing up a new user and then having an admin edit a user field later. You need to validate a password on a user object during registration but you won't show the password field to the admin editing the user details.

The choices for getting around these are all sub-optimal. I've worked on this problem for 3 projects now and implementing the following solutions has never been clean and usually frustrating. I'm going to try and be practical and forget all the DDD/db/model/hotnessofthemonth discussions everybody else is having.

1) Multiple View Models Having viewmodels that are almost the same violates the DRY principal but I feel the costs of this approach are really low. Usually violating DRY amps up maintenance costs but IMHO the costs for this are the lowest and don't amount to much. Hypothetically speaking you don't change how max number characters the LastName field can have very often.

2) Dynamic Metadata There are hooks in MVC 2 for providing your own metadata for a model. With this approach you could have whatever your using to provide metadata exclude certain fields based on the current HTTPRequest and therefore Action and Controller. I've used this technique to build a database driven permissions system which goes to the DB and tells the a subclass of the DataAnnotationsMetadataProvider to exclude properties based values stored in the database.

This technique is working great atm but the only problem is validating with UpdateModel(). To solve this problem we created a SmartUpdateModel() method which also goes to the database and automatically generates the exclude string[] array so that any non-permissisable fields aren't validated. We of course cached this for performance reasons so its not bad.

Just want to reiterate that we used [ValidationAttributes] on our models and then superceeded them with new rules on runtime. The end result was that the [Required] User.LastName field wasn't validated if the user didn't have permission to access it.

3) Crazy Interface Dynamic Proxy Thing The last technique I tried to was to use interfaces for ViewModels. The end result was I had a User object that inherited from interfaces like IAdminEdit and IUserRegistration. IAdminEdit and IUserRegistration would both contain DataAnnotation attributes that performed all the context specific validation like a Password property with the interfaces.

This required some hackery and was more an academic exercise than anything else. The problem with 2 and 3 is that UpdateModel and the DataAnnotationsAttribute provider needed to be customized to be made aware of this technique.

My biggest stumbling block was I didn't ever want to send the whole user object to the view so I ended up using dynamic proxies to create runtime instances of IAdminEdit

Now I understand this is a very xVal specific question but all of the roads to dynamic validation like this lead to customization of the internal MVC Metadata providers. Since all the metadata stuff is new nothing is that clean or simple to do at this point. The work you'd have to do to customize MVC's validation behavior isn't hard but requires some in depth knowledge of how all of the internals work.

这篇关于验证重用定制的ViewModels属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆