.net MVC3 условно проверяющее свойство, которое опирается на свойство родительского объекта - PullRequest
1 голос
/ 06 октября 2011

У меня есть следующая 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, нотолько если требуется текущая комната.Это можно узнать, посмотрев на свойство NumberOfRooms объекта StayDetails.

Мой пользовательский валидатор до сих пор:

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 не имеет конструктора без параметров.

Любые предложения?

Большое спасибо,David

Ответы [ 3 ]

2 голосов
/ 06 октября 2011

Вы должны определить аннотацию проверки на StayDetails class вместо RoomDetail. Таким образом, у вас будут все значения, NumberOfRooms, список комнат и их соответствующие RoomNumber и NumberOfAdults. Измените свой валидатор соответственно.

0 голосов
/ 19 сентября 2018

Мне удалось решить эту проблему с помощью пользовательских связывателей.Вам нужно будет добавить свойство для ссылки на родительский объект, например NumberOfRooms.В моем случае я фактически создал делегат, который ссылался на подпрограмму в родительском объекте.Заранее извиняюсь за проблемы с кодом и форматированием VB, которые возникают у меня с stackoverflow.

  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

Далее я создаю пользовательский связыватель для родительского объекта: Это также делает несколько вещей:

a) Сохраните родительский объект в controlcontext, чтобы он мог использоваться дочерним объектом.б) Повторная проверка дочернего объекта.

Открытый класс 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

Конечный класс

Украсьте свой подкласс соответствующим образом

Public Class RoomDetail

Установите свой контроллер так, чтобы он использовал пользовательское связующее:

(имя вашего параметра здесь)

Убедитесь, что для вашего валидатора установлено значение SUCCESS, если вы проверяете его без заполненного свойства NumberOfRooms.Привязка выполнит ваши валидаторы дважды для вашего дочернего класса.Первый раз, когда свойство заполнено, а затем снова, когда свойство заполнено.

0 голосов
/ 06 октября 2011

Вы можете попробовать сделать это с помощью FluentValidation PropertyValidator .
Пишите меньше, делайте больше ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...