ASP.NET MVC绑定与远程验证

时间:2021-09-16 17:53:53

I have a model ModelA with a member toBeRemoteChecked and a model MapToA with a member valueToMap. Whenever I create an instance of ModelA, I also need an instance of MapToA, so I have a model CreateModelA which includes a member modelA and a member valueToMap. When the form is submitted, I add the modelA to the database table ModelA and create and add an instance to MapToA which consists of an id of modelA and the valueToMap. In Terms of code

我有一个ModelA,一个成员toBeRemoteChecked和一个模型MapToA与成员valueToMap。每当我创建一个ModelA实例时,我还需要一个MapToA实例,所以我有一个模型CreateModelA,它包含一个成员modelA和一个成员valueToMap。提交表单时,我将modelA添加到数据库表ModelA中,并创建并向MapToA添加一个实例,该实例由modelA的id和valueToMap组成。在代码方面

public class ModelA
{
    [Key]
    public int ID { get; set; }
    [Required, Remote("isValid", "MyController", ErrorMessage = "not valid")]
    public string toBeRemoteChecked { get; set; }
}

public class MapToA
{
    [Key]
    public int Map_ID { get; set; }
    [Required]
    public int modelAID { get; set; }
    [Required]
    public int valueToMap { get; set; }
}

public class CreateModelA
{
    public ModelA modelA { get; set; };
    public int valueToMap { get; set; };
}

When I edit an instance of ModelA, values in MapToA don't matter (and in most cases there's more than one instance of mapToA with the same modelA id), but the remote validation of toBeRemoteChecked remains important.

当我编辑ModelA的实例时,MapToA中的值无关紧要(并且在大多数情况下,mapToA的多个实例具有相同的modelA id),但toBeRemoteChecked的远程验证仍然很重要。

My Problem: binding for the validation method:

我的问题:验证方法的绑定:

public ActionResult isValid(string toBeRemoteChecked) { ... }

If I leave it as it is, it is working when editing a ModelA, but not when I'm creating a ModelA via CreateModelA (I always get null value in toBeRemoteChecked). When I use the BindPrefix

如果我保持原样,它在编辑ModelA时有效,但在我通过CreateModelA创建ModelA时却没有(我总是在toBeRemoteChecked中得到null值)。当我使用BindPrefix时

public ActionResult isValid([Bind(Prefix = "modelA.toBeRemoteChecked")] string toBeRemoteChecked) { ... }

it is working when I create a ModelA, but not when I'm editing it.

它在我创建ModelA时工作,但在我编辑它时却没有。

When I try to change the "name" in the Create.cshtml by adding a ... @Name = "toBeRemoteChecked" ... (instead of the modelA.toBeRemoteChecked that's created by the HTML helper) in the htmlAttributes of the @Html.TextBoxFor, then validation is working, but the binding of the value to the table get's lost and I get the error when the values are saved to the database (null value).

当我尝试通过在@Html的htmlAttributes中添加... @Name =“toBeRemoteChecked”...(而不是由HTML帮助程序创建的modelA.toBeRemoteChecked)来更改Create.cshtml中的“名称”时.TextBoxFor,然后验证工作正常,但是值与表的绑定会丢失,当值保存到数据库时我得到错误(空值)。

So, how do I achieve the different binding for creating and editing?

那么,如何实现创建和编辑的不同绑定?

So far, my workaround is to make ModelA and CreateModelA : IValidatableObject and check the member toBeRemoteChecked in my public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) method. But that one displays the error messages on top of the form and not at the place of the TextFor box.

到目前为止,我的解决方法是创建ModelA和CreateModelA:IValidatableObject并在我的公共IEnumerable Validate(ValidationContext validationContext)方法中检查成员toBeRemoteChecked。但是那个在表单顶部而不是在TextFor框的位置显示错误消息。

So: best solution: how to do the binding that the remote validation works in both cases?

那么:最佳解决方案:如何在两种情况下进行远程验证的绑定?

Second best: how to display the error messages of IValidatableObject near the object where it belongs to (and get the error messages right at hand, not after submitting)

第二好:如何在它所属的对象附近显示IValidatableObject的错误消息(并在手头获取错误消息,而不是在提交后)

Different ideas or solutions: welcome.

不同的想法或解决方案:欢迎。

Thanks.

2 个解决方案

#1


3  

An interesting issue, and similar to this question, which as an result I reported a issue at Codeplex, but it has not been resolved yet. The link includes a suggested modification to the jquery.validate.js file which would solve this (it strips the prefix) but that means you would need to maintain it whenever you update the script so not really desirable.

一个有趣的问题,与此问题类似,因此我在Codeplex报告了一个问题,但尚未解决。该链接包括对jquery.validate.js文件的建议修改,该文件将解决此问题(它会删除前缀),但这意味着您需要在更新脚本时进行维护,因此不太理想。

One option would be to change CreateModelA to inherit from ModelA and just add the int valueToMap property so that you never have a prefix - your always using @Html.TextBoxFor(m => m.toBeRemoteChecked) instead of @Html.TextBoxFor(m => m.modelA.toBeRemoteChecked)

一种选择是将CreateModelA更改为继承自ModelA,只需添加int valueToMap属性,这样就不会有前缀 - 总是使用@ Html.TextBoxFor(m => m.toBeRemoteChecked)而不是@Html.TextBoxFor(m = > m.modelA.toBeRemoteChecked)

Also, [Remote] is client side only validation, which means you still need to perform the validation in the server when you post. So you could just accept that you don't have client side validation for the property, and instead add a ModelState error in the POST methods(s) for the property and return the view so that its displayed in the associated ValidationMessageFor() element

此外,[Remote]是仅客户端验证,这意味着您仍需要在发布时在服务器中执行验证。所以你可以接受你没有对属性进行客户端验证,而是在属性的POST方法中添加ModelState错误并返回视图,以便它显示在关联的ValidationMessageFor()元素中

Side note: The fact your model has a [Key] attribute suggests this is a data model, not a view model, and [Remote] is a view specific attribute. You should be using view models, especially when editing data. (refer What is ViewModel in MVC?)

附注:您的模型具有[Key]属性的事实表明这是数据模型,而不是视图模型,[Remote]是视图特定属性。您应该使用视图模型,尤其是在编辑数据时。 (参考MVC中的ViewModel是什么?)

#2


2  

I found a solution without inheritance (and without view models) that solves my binding problem with just little change to my code.

我找到了一个没有继承(并且没有视图模型)的解决方案,只需对代码进行少量更改即可解决我的绑定问题。

There's two ways of binding for remote validation, you can either just pass the member that has to be remote checked

有两种绑定远程验证的方法,您可以只传递必须远程检查的成员

public ActionResult isValid(string toBeRemoteChecked) { ... }

or you can pass the instance of the class of that member.

或者您可以传递该成员的类的实例。

public ActionResult isValid(ModelA modelA) { ... }

Inside the second variant, of course, you have to replace toBeRemoteChecked with modelA.toBeRemoteChecked. On this second version the binding works in both cases - when editing and also when creating my instance of ModelA in the context above. In order to make the binding work, it's crucial that the parameter name of the remote validation method matches the member name in the CreateModelA, i.e. modelA in my case.

当然,在第二个变体中,您必须使用modelA.toBeRemoteChecked替换toBeRemoteChecked。在第二个版本中,绑定适用于两种情况 - 编辑时以及在上面的上下文中创建ModelA实例时。为了使绑定工作,远程验证方法的参数名称与CreateModelA中的成员名称匹配至关重要,在我的情况下是modelA。

In case you have a very complex model, you can just initialize the parameter modelA with the members you want to use by using bind/include, i.e. in my case I'd use

如果你有一个非常复杂的模型,你可以使用bind / include初始化参数modelA和你想要使用的成员,即在我的情况下我会使用

public ActionResult isValid([Bind(Include = "toBeRemoteChecked")] ModelA modelA) { ... }

By default (without Include), all other members will remain null or have a default value - so you need to use Include only if you need other members for validation as well - in my case, I would have the same when omitting the Include)

默认情况下(没有Include),所有其他成员将保持为null或具有默认值 - 因此,只有在需要其他成员进行验证时才需要使用Include - 在我的情况下,省略Include时也会如此

#1


3  

An interesting issue, and similar to this question, which as an result I reported a issue at Codeplex, but it has not been resolved yet. The link includes a suggested modification to the jquery.validate.js file which would solve this (it strips the prefix) but that means you would need to maintain it whenever you update the script so not really desirable.

一个有趣的问题,与此问题类似,因此我在Codeplex报告了一个问题,但尚未解决。该链接包括对jquery.validate.js文件的建议修改,该文件将解决此问题(它会删除前缀),但这意味着您需要在更新脚本时进行维护,因此不太理想。

One option would be to change CreateModelA to inherit from ModelA and just add the int valueToMap property so that you never have a prefix - your always using @Html.TextBoxFor(m => m.toBeRemoteChecked) instead of @Html.TextBoxFor(m => m.modelA.toBeRemoteChecked)

一种选择是将CreateModelA更改为继承自ModelA,只需添加int valueToMap属性,这样就不会有前缀 - 总是使用@ Html.TextBoxFor(m => m.toBeRemoteChecked)而不是@Html.TextBoxFor(m = > m.modelA.toBeRemoteChecked)

Also, [Remote] is client side only validation, which means you still need to perform the validation in the server when you post. So you could just accept that you don't have client side validation for the property, and instead add a ModelState error in the POST methods(s) for the property and return the view so that its displayed in the associated ValidationMessageFor() element

此外,[Remote]是仅客户端验证,这意味着您仍需要在发布时在服务器中执行验证。所以你可以接受你没有对属性进行客户端验证,而是在属性的POST方法中添加ModelState错误并返回视图,以便它显示在关联的ValidationMessageFor()元素中

Side note: The fact your model has a [Key] attribute suggests this is a data model, not a view model, and [Remote] is a view specific attribute. You should be using view models, especially when editing data. (refer What is ViewModel in MVC?)

附注:您的模型具有[Key]属性的事实表明这是数据模型,而不是视图模型,[Remote]是视图特定属性。您应该使用视图模型,尤其是在编辑数据时。 (参考MVC中的ViewModel是什么?)

#2


2  

I found a solution without inheritance (and without view models) that solves my binding problem with just little change to my code.

我找到了一个没有继承(并且没有视图模型)的解决方案,只需对代码进行少量更改即可解决我的绑定问题。

There's two ways of binding for remote validation, you can either just pass the member that has to be remote checked

有两种绑定远程验证的方法,您可以只传递必须远程检查的成员

public ActionResult isValid(string toBeRemoteChecked) { ... }

or you can pass the instance of the class of that member.

或者您可以传递该成员的类的实例。

public ActionResult isValid(ModelA modelA) { ... }

Inside the second variant, of course, you have to replace toBeRemoteChecked with modelA.toBeRemoteChecked. On this second version the binding works in both cases - when editing and also when creating my instance of ModelA in the context above. In order to make the binding work, it's crucial that the parameter name of the remote validation method matches the member name in the CreateModelA, i.e. modelA in my case.

当然,在第二个变体中,您必须使用modelA.toBeRemoteChecked替换toBeRemoteChecked。在第二个版本中,绑定适用于两种情况 - 编辑时以及在上面的上下文中创建ModelA实例时。为了使绑定工作,远程验证方法的参数名称与CreateModelA中的成员名称匹配至关重要,在我的情况下是modelA。

In case you have a very complex model, you can just initialize the parameter modelA with the members you want to use by using bind/include, i.e. in my case I'd use

如果你有一个非常复杂的模型,你可以使用bind / include初始化参数modelA和你想要使用的成员,即在我的情况下我会使用

public ActionResult isValid([Bind(Include = "toBeRemoteChecked")] ModelA modelA) { ... }

By default (without Include), all other members will remain null or have a default value - so you need to use Include only if you need other members for validation as well - in my case, I would have the same when omitting the Include)

默认情况下(没有Include),所有其他成员将保持为null或具有默认值 - 因此,只有在需要其他成员进行验证时才需要使用Include - 在我的情况下,省略Include时也会如此