.net MVC3有条件地验证依赖于父对象属性的属性

时间:2011-10-06 13:23:53

标签: .net asp.net-mvc-3 validation

我有以下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对象没有无参数构造函数。

有什么建议吗?

非常感谢,
大卫

3 个答案:

答案 0 :(得分:2)

您应该在StayDetails class而不是RoomDetail上定义验证注释。这样,您将拥有所有值,NumberOfRooms,房间列表以及它们各自的RoomNumber和NumberOfAdults。相应地更改验证器。

答案 1 :(得分:0)

您可以尝试使用FluentValidation PropertyValidator进行此操作 写得少做多了......

答案 2 :(得分:0)

我能够通过使用自定义活页夹解决此问题。您将需要添加一个属性以引用回父对象,例如NumberOfRooms。就我而言,我实际上创建了一个委托,该委托引用了父对象中的例程。预先为我对stackoverflow遇到的VB代码和格式问题表示歉意。

  1. 在子对象上创建NumberOfRooms属性。
  2. 为子对象类创建一个自定义模型活页夹。该模型绑定器将执行以下几项操作: a)为NumberOfRooms插入一个值(在我的情况下,我设置了一个委托) b)将元数据/密钥信息存储在控制器字典中,以供以后重新验证。

例如:

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
  1. 接下来,我为父对象创建一个自定义活页夹:这还可以做一些事情:

    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
    

    结束课程

  2. 适当地装饰您的子类

    公共类RoomDetail

  3. 设置您的控制器,使其将使用自定义活页夹:

    (在此处输入参数名称)

  4. 如果在未填写NumberOfRooms属性的情况下进行验证,请确保将验证器设置为SUCCESS。绑定将为您的子类执行两次验证器。在属性填充之前第一次,然后在属性填充之后再次。