简体   繁体   English

多态绑定器:当自定义模型绑定包装在另一个类中时,验证不起作用

[英]Polymorphic binder: validation not working when custom model bound is wrapped in another class

I have changed a little the Polymorphic binding example from this article .我对本文中的多态绑定示例进行了一些更改。

  1. Add [Required] attribute to the CPUIndex and ScreenSize properties of Laptop respectively SmartPhone classes.分别为LaptopSmartPhone类的CPUIndexScreenSize属性添加[Required]属性。
  2. Run example and create whatever kind of device without filling in CPU index nor Screen size.运行示例并创建任何类型的设备,而无需填写 CPU 索引或屏幕大小。
  3. It runs correctly - model is bound and validated (shows you the error, that "CPU index / Screen size are required".它运行正常 - 模型已绑定并验证(向您显示错误,即“需要 CPU 索引/屏幕大小”。

So far ok.到目前为止还好。

Now add new class:现在添加新类:

public class DeviceWrapper
{
    public Device Device { get; set; }
}

and modify the AddDevice.cshtml.cs file:并修改AddDevice.cshtml.cs文件:

...
[BindProperty]
public DeviceWrapper Device { get; set; } // model type changed here

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    switch (Device.Device) // added Device. prefix to respect new model structure
    {
        case Laptop laptop:
            Message = $"You added a Laptop with a CPU Index of {laptop.CPUIndex}.";
            break;

        case SmartPhone smartPhone:
            Message = $"You added a SmartPhone with a Screen Size of {smartPhone.ScreenSize}.";
            break;
    }

    return RedirectToPage("/Index");
}

Modify also the page AddDevice.cshtml to respect new model structure.还修改页面AddDevice.cshtml以尊重新的模型结构。 Ie prefix every Device.{prop} with Device.即为每个Device.{prop}加上Device.前缀Device. in all name attributes.在所有name属性中。 Example:例子:

<select id="Device_Kind" name="Device.Device.Kind" asp-items="Model.DeviceKinds" class="form-control"></select>

Now run the application.现在运行应用程序。 Put breakpoint into the AddDeviceModel.OnPost method.将断点放入AddDeviceModel.OnPost方法中。 Do the same as in the first example - create device without filling neither CPU index nor Screen size.执行与第一个示例相同的操作 - 创建设备而不填充 CPU 索引和屏幕大小。 Check the value of ModelState.IsValid which is now true .检查ModelState.IsValid现在为true的值。 Model is bound, but not validated.模型已绑定,但未验证。 What should I do to apply validation also for this wrapped model.我应该怎么做才能对这个包装模型应用验证。

Try it out on my fork of sample: https://github.com/zoka-cz/AspNetCore.Docs/tree/master/aspnetcore/mvc/advanced/custom-model-binding/3.0sample/PolymorphicModelBinding在我的示例分支上尝试一下: https : //github.com/zoka-cz/AspNetCore.Docs/tree/master/aspnetcore/mvc/advanced/custom-model-binding/3.0sample/PolymorphicModelBinding

I have found, there are more problems:我发现,还有更多的问题:

Firstly, the official example here and also in the sample source codes contains the error:首先, 这里和示例源代码中的官方示例包含错误:

// Setting the ValidationState ensures properties on derived types are correctly 
bindingContext.ValidationState[newBindingContext.Result] = new ValidationStateEntry
{ Metadata = modelMetadata };

which should be这应该是

// Setting the ValidationState ensures properties on derived types are correctly 
bindingContext.ValidationState[newBindingContext.Result.Model] = new ValidationStateEntry
{ Metadata = modelMetadata };

(note the indexer - it should be the model itself. Model instance is the key, by which the ValidationState dictionary is searched). (注意索引器 - 它应该是模型本身。模型实例是键,通过它搜索 ValidationState 字典)。

But, it doesn't solve the original issue with no validating the wrapped model.但是,它没有解决原始问题而不验证包装模型。 There is another problem, which I believe is error in ASP.NET Core (already reported - see for details).还有另一个问题,我认为是 ASP.NET Core 中的错误(已报告- 有关详细信息,请参阅)。 The abstract Device class is not validated, because it gets skipped (for which reason?) because DeviceWrapper says that there are no validators for its sub-properties, because it doesn't take into account the metadata for real type ( Laptop or SmartPhone ) but only the metadata for abstract type Device , which really do not have validators.抽象Device类未被验证,因为它被跳过(出于什么原因?)因为DeviceWrapper说它的子属性没有验证器,因为它没有考虑真实类型( LaptopSmartPhone )的元数据但只有抽象类型Device的元数据,它确实没有验证器。

This finding let me to the solution I believe is better.这个发现让我找到了我认为更好的解决方案。 I must force the validator to validate the Device.我必须强制验证器验证设备。 I must say that the property of type Device has validator.我必须说Device类型的属性具有验证器。 This may be achieved either by implementing IValidatableObject on the abstract class Device :这可以通过在抽象类Device上实现IValidatableObject来实现:

public abstract class Device : IValidatableObject
{
    public string Kind { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        yield return ValidationResult.Success; // just to force validator to validate according to the correct metadata
    }
}

It may happen you cannot change the abstract class, then you may find the second way more viable:可能会发生您无法更改抽象类的情况,然后您可能会发现第二种方法更可行:

public class ForceValidationAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        return true;
    }
}
public class DeviceWrapper
{
    [ForceValidation] // this will force the validator to work according to the correct metadata
    public Device Device { get; set; }
}

Both methods are not good, as I believe, it should be fixed in ASP.NET Core, but as workaround it seems to me more clean solution, as the produced ModelState is correct.这两种方法都不好,我相信它应该在 ASP.NET Core 中修复,但作为解决方法,它在我看来更干净的解决方案,因为生成的 ModelState 是正确的。

You can try to use TryValidateModel: cshtml.cs:你可以尝试使用TryValidateModel:cshtml.cs:

public IActionResult OnPost()
        {
            TryValidateModel(Device.Device);
            if (!ModelState.IsValid) {
                return Page();
            }

            switch (Device.Device)
            {
                case Laptop laptop:
                    Message = $"You added a Laptop with a CPU Index of {laptop.CPUIndex}.";
                    break;

                case SmartPhone smartPhone:
                    Message = $"You added a SmartPhone with a Screen Size of {smartPhone.ScreenSize}.";
                    break;
            }

            return RedirectToPage("/Index");
        }

result:结果: 在此处输入图片说明

If you still cannot work,add ModelState.Clear();如果仍然无法工作,请添加ModelState.Clear(); before TryValidateModel(Device.Device);TryValidateModel(Device.Device);之前TryValidateModel(Device.Device);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM