MVC 4自定义验证属性在服务器端触发两次

use*_*835 2 validation asp.net-mvc

我正在使用自Required继承的自定义验证requiredconditional,以有条件地需要某些字段。除非有一次,这种方法在任何地方都可以很好地工作,我无法弄清楚发生了什么。它为模型中的一个属性调用两次IsValid方法(客户端验证工作正常)。模型使用此属性具有2个属性,但只有一个存在问题。最初,我认为这是因为我的条件之一是一个复选框,并且它同时回发了已检查的值和隐藏的值,但是我尝试使用单选按钮代替,甚至使用隐藏的值,但在所有情况下都具有相同的结果。这是我的观点(简化测试):

@ModelType List(Of eLADNETBusiness.AdditionalInterest)

@Code
    ViewData("Title") = "Bind Coverage Entry"
End Code

<h2>Bind Coverage Entry</h2>
@Using Html.BeginForm()
    @Html.AntiForgeryToken() 
    @Html.Hidden("hullValue", ViewBag.HullValue)
    Dim currentCount As Integer = 0
    @For count As Integer = 0 To Model.Count - 1
            currentCount = count
             @<div class="editor-label">
                 @Html.LabelFor(Function(model) model(currentCount).LienholderAmount)
              </div>
             @<div class="editor-field">
                @Html.EditorFor(Function(model) model(currentCount).LienholderAmount)
                @Html.ValidationMessageFor(Function(model) model(currentCount).LienholderAmount)
             </div>
             @<div>
                @Html.EditorFor(Function(model) model(currentCount).Lienholder90Percent)
             </div>
     Next
    @<p>
        <input type="submit" value="Continue" />
    </p>

End Using
Run Code Online (Sandbox Code Playgroud)

这是我的模型(简化测试):

<DataContract()> _
Public Class AdditionalInterest
<DataMember()> _
Public Property ID As Integer = 0 

<RequiredConditional("Lienholder90Percent", False, ErrorMessage:="Enter Breach of Warranty lienamount or select 90 percent of insured value")> _
<Display(Name:="Lienholder Amount")> _
<DataMember()> _
Public Property LienholderAmount As Nullable(Of Integer)    

<DataMember()> _
Public Property Lienholder90Percent As Boolean
Run Code Online (Sandbox Code Playgroud)

最终班

和我所需的条件属性:

Imports System.Collections.Generic
Imports System.Linq
Imports System.ComponentModel.DataAnnotations
Imports System.Web.Mvc
Imports System.Collections
Imports System.Text

Public Class RequiredConditional
Inherits RequiredAttribute
Implements IClientValidatable

Private Property PropertyNames() As String()
Private Property DesiredValues() As Object()

Public Sub New(comparePropertyNames As String(), comparePropertyDesiredValues As Object())
    PropertyNames = comparePropertyNames
    DesiredValues = comparePropertyDesiredValues
End Sub

Public Sub New(comparePropertyNames As String, comparePropertyDesiredValues As Object)
    PropertyNames = New String() {comparePropertyNames}
    DesiredValues = New String() {comparePropertyDesiredValues}
End Sub

Protected Overrides Function IsValid(value As Object, context As ValidationContext) As ValidationResult
    Dim instance As Object = context.ObjectInstance
    Dim type As Type = instance.GetType()
    Dim propertyvalue As Object
    Dim trueConditions As Integer = 0
    For count As Integer = 0 To PropertyNames.Count - 1
        propertyvalue = type.GetProperty(PropertyNames(count)).GetValue(instance, Nothing)
        If Not propertyvalue Is Nothing Then
            If DesiredValues.Count >= count + 1 Then
                If propertyvalue.ToString() = DesiredValues(count).ToString() Then
                    trueConditions += 1
                End If
            End If
        End If
    Next
    'if all conditions are met, validate value
    If trueConditions = PropertyNames.Count And trueConditions = DesiredValues.Count Then
        Dim result As ValidationResult = MyBase.IsValid(value, context)
        Return result
    End If

    Return ValidationResult.Success
End Function

Public Function GetClientValidationRules(metadata As System.Web.Mvc.ModelMetadata, context As System.Web.Mvc.ControllerContext) As System.Collections.Generic.IEnumerable(Of System.Web.Mvc.ModelClientValidationRule) _
    Implements System.Web.Mvc.IClientValidatable.GetClientValidationRules

    Dim results As New List(Of ModelClientValidationRule)
    Dim rule = New ModelClientValidationRule With {.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()), .ValidationType = "requiredif"}

    Dim depProp As String = String.Empty 

    Dim sbProp As New StringBuilder()
    Dim sbTarget As New StringBuilder()
    Dim helper As New ValidationHelper

    For count As Integer = 0 To PropertyNames.Count - 1
        Dim prop As String = PropertyNames(count)
        depProp = helper.BuildDependentPropertyName(metadata, TryCast(context, ViewContext), prop)
        sbProp.AppendFormat("|{0}", depProp)

        Dim targetValue As String = String.Empty
        If DesiredValues.Count >= count + 1 Then
            targetValue = (If(DesiredValues(count), "")).ToString()
        End If

        If DesiredValues(count).GetType() = GetType(Boolean) Then
            targetValue = DesiredValues(count).ToString.ToLower
        End If

        sbTarget.AppendFormat("|{0}", targetValue)

    Next

    rule.ValidationParameters.Add("dependentproperty", sbProp.ToString().TrimStart("|"))
    rule.ValidationParameters.Add("targetvalue", sbTarget.ToString().TrimStart("|"))


    results.Add(rule)
    Return results

End Function

End Class
Run Code Online (Sandbox Code Playgroud)

因此,当我单击提交并调试到requiredconditional属性时,lienholderamount属性将两次击中IsValid。第一次命中时,Lienholder90Percent的值是False,即使在模型中它是true且在表单上是正确的(并通过了客户端验证),所以此时验证失败。然后再次命中,Lienholder90Percent为True(正确)并通过验证。但是由于它在第一个失败,所以它仍然失败并显示错误消息。您会注意到该模型是一个列表,但是出于测试目的,我只发送了一个,仍然得到相同的结果。无法弄清楚为什么仅此一个属性会发生这种情况。希望这很简单,我只是看不到。我花了整整一天的时间试图弄清楚这一点。就像我说的那样,我使用了这个属性很多并且效果很好。

SxM*_*xMT 5

ValidationAttribute而不是RequiredAttribute继承。

在填充属性后立即调用RequiredAttribute的IsValid,而在填充整个模型后调用ValidationAttribute的IsValid(这对于更复杂的验证是有意义的)。

  • 这应该被接受为正确答案。 (2认同)