我有以下ViewModel:
public class StayDetails
{
public int NumberOfRooms { get; set; }
public IList<RoomDetail> Rooms { get;set; }
}
public class RoomDetail
{
public int RoomNumber { get; set; }
[MinIfRoomRequired("StayDetails.NumberOfRooms", "RoomNumber", 1]
public int NumberOfAdults { get;set; }
}
我要做的是创建一个自定义验证器,它将验证房间内成人的数量,并确保至少有1个,但前提是需要当前房间。通过查看StayDetails对象上的NumberOfRooms属性可以了解这一点。
我的自定义验证器到目前为止:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// get a reference to the depended properties
var containerType = validationContext.ObjectInstance.GetType();
var requiredRoomsField = containerType.GetProperty(RequiredRoomsPropertyName);
var roomNumberField = containerType.GetProperty(RoomNumberPropertyName);
if (requiredRoomsField != null && roomNumberField != null)
{
// get the value of the dependent properties
var requiredRoomsValue = requiredRoomsField.GetValue(validationContext.ObjectInstance, null);
var roomNumberValue = roomNumberField.GetValue(validationContext.ObjectInstance, null);
... (remaining logic to test values) ...
我遇到的问题是我无法访问NumberOfRooms属性,validationContext.ObjectInstance没有任何对父对象的引用。我想在对象初始化期间将StayDetails对象的引用添加到RoomDetails对象上,因此我可以从那里引用该属性,但模型绑定不允许这样做,因为RoomDetail对象没有无参数构造函数。
有什么建议吗?
非常感谢,
大卫
答案 0 :(得分:2)
您应该在StayDetails class
而不是RoomDetail上定义验证注释。这样,您将拥有所有值,NumberOfRooms,房间列表以及它们各自的RoomNumber和NumberOfAdults。相应地更改验证器。
答案 1 :(得分:0)
您可以尝试使用FluentValidation PropertyValidator进行此操作 写得少做多了......
答案 2 :(得分:0)
我能够通过使用自定义活页夹解决此问题。您将需要添加一个属性以引用回父对象,例如NumberOfRooms。就我而言,我实际上创建了一个委托,该委托引用了父对象中的例程。预先为我对stackoverflow遇到的VB代码和格式问题表示歉意。
例如:
Public Class RoomDetail_Binder
Inherits DefaultModelBinder
Protected Overrides Function CreateModel(controllerContext As ControllerContext, bindingContext As ModelBindingContext, modelType As Type) As Object
Dim theObj As Quote_Equipment_Model = MyBase.CreateModel(controllerContext, bindingContext, modelType)
Dim theParent As StayDetails= controllerContext.HttpContext.Items("StayDetails")
If Not IsNothing(theParent) Then
theObj.NumberOfRooms=theParent.NumberOfRooms
End If
Return theObj
End Function
Protected Overrides Sub OnModelUpdated(controllerContext As ControllerContext, bindingContext As ModelBindingContext)
MyBase.OnModelUpdated(controllerContext, bindingContext)
Dim theMetadataList As List(Of MetaDataPair)
If Not controllerContext.HttpContext.Items.Contains("MetadataList") Then
theMetadataList = New List(Of MetaDataPair)
controllerContext.HttpContext.Items.Add("MetadataList", theMetadataList)
Else
theMetadataList = controllerContext.HttpContext.Items("MetadataList")
End If
theMetadataList.Add(New MetaDataPair With {.Metadata = bindingContext.ModelMetadata, .BindingModelName = bindingContext.ModelName})
End Sub
End Class
请注意,MetadataList只是
Public Class MetaDataPair
Public Property BindingModelName As String
Public Property Metadata As ModelMetadata
End Class
接下来,我为父对象创建一个自定义活页夹:这还可以做一些事情:
a)将父对象存储在controllercontext中,以便子对象可以使用它。 b)重新验证子对象。
公共类StayDetails_Binder 继承DefaultModelBinder
Protected Overrides Function CreateModel(controllerContext As ControllerContext, bindingContext As ModelBindingContext, modelType As Type) As Object
Dim theObj As StayDetails = MyBase.CreateModel(controllerContext, bindingContext, modelType)
controllerContext.HttpContext.Items("StayDetails") = theObj
Return theObj
End Function
Public Overrides Function BindModel(controllerContext As ControllerContext, bindingContext As ModelBindingContext) As Object
Dim theObj As StayDetails = MyBase.BindModel(controllerContext, bindingContext)
Dim theMetadataList As List(Of MetaDataPair) = CType(controllerContext.HttpContext.Items("MetadataList"), List(Of MetaDataPair))
For Each Metadata In theMetadataList
For Each result As ModelValidationResult In ModelValidator.GetModelValidator(Metadata.Metadata, controllerContext).Validate(Nothing)
Dim key As String = CreateSubPropertyName(Metadata.BindingModelName, result.MemberName)
If Not bindingContext.ModelState(key).Errors.Any(Function(ent) ent.ErrorMessage = result.Message) Then
bindingContext.ModelState.AddModelError(key, result.Message)
End If
Next
Next
Return theObj
End Function
结束课程
适当地装饰您的子类
设置您的控制器,使其将使用自定义活页夹:
如果在未填写NumberOfRooms属性的情况下进行验证,请确保将验证器设置为SUCCESS。绑定将为您的子类执行两次验证器。在属性填充之前第一次,然后在属性填充之后再次。