[英]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 .我对本文中的多态绑定示例进行了一些更改。
[Required]
attribute to the CPUIndex
and ScreenSize
properties of Laptop
respectively SmartPhone
classes.分别为Laptop
和SmartPhone
类的CPUIndex
和ScreenSize
属性添加[Required]
属性。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
说它的子属性没有验证器,因为它没有考虑真实类型( Laptop
或SmartPhone
)的元数据但只有抽象类型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");
}
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.