it-swarm.com.de

ASP.NET MVC Bedingte Validierung

Wie können Datenanmerkungen verwendet werden, um eine bedingte Validierung des Modells durchzuführen?

Nehmen wir beispielsweise an, wir haben folgendes Modell (Person und Senior):

public class Person
{
    [Required(ErrorMessage = "*")]
    public string Name
    {
        get;
        set;
    }

    public bool IsSenior
    {
        get;
        set;
    }

    public Senior Senior
    {
        get;
        set;
    }
}

public class Senior
{
    [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
    public string Description
    {
        get;
        set;
    }
}

Und die folgende Ansicht:

<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>

<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>

<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>

Ich möchte das bedingte erforderliche Feld "Senior.Description" sein, das auf der Auswahl der Eigenschaft "IsSenior" basiert (true -> required). Wie Implementieren der bedingten Validierung in ASP.NET MVC 2 mit Datenanmerkungen?

118
Peter Stegnar

Ich habe dieses Problem gelöst, indem ich das "ModelState" - Wörterbuch handhabte, das im Controller enthalten ist. Das ModelState-Wörterbuch enthält alle Mitglieder, die überprüft werden müssen.

Hier ist die Lösung:

Wenn Sie eine bedingte Validierung basierend auf einem Feld implementieren müssen (z. B. wenn A = true ist, ist B erforderlich), , während die Fehlermeldungsfunktion auf Eigenschaftsebene beibehalten wird auf Objektebene) können Sie dies erreichen, indem Sie "ModelState" behandeln, indem Sie einfach unerwünschte Validierungen daraus entfernen.

... in irgendeiner Klasse ... 

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

[Required(ErrorMessage = "*")] 
public string DepentedProperty
{
  get;
  set;
}

... der Unterricht geht weiter ...

... in einer Controller-Aktion ...

if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
   this.ModelState.Remove("DepentedProperty");
}

...

Damit erreichen wir eine bedingte Validierung, während alles andere gleich bleibt.


UPDATE:

Dies ist meine endgültige Implementierung: Ich habe eine Schnittstelle für das Modell und das Aktionsattribut verwendet, das das Modell validiert, das die Schnittstelle implementiert. Die Schnittstelle schreibt die Validate-Methode (ModelStateDictionary modelState) vor. Das Attribut "action" ruft für IValidatorSomething nur Validate (modelState) auf.

Ich wollte diese Antwort nicht verkomplizieren, daher erwähnte ich nicht die endgültigen Implementierungsdetails (die am Ende im Produktionscode eine Rolle spielen).

61
Peter Stegnar

es gibt eine viel bessere Möglichkeit, bedingte Validierungsregeln in MVC3 hinzuzufügen. Lassen Sie Ihr Modell IValidatableObject erben und implementieren Sie die Validate-Methode:

public class Person : IValidatableObject
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
        if (IsSenior && string.IsNullOrEmpty(Senior.Description)) 
            yield return new ValidationResult("Description must be supplied.");
    }
}

weitere Informationen finden Sie unter http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

141
viperguynaz

Ich hatte gestern das gleiche Problem, aber ich habe es auf eine sehr saubere Weise gemacht, die sowohl für die Client- als auch für die Server-Validierung funktioniert.

Bedingung: Basierend auf dem Wert einer anderen Eigenschaft im Modell möchten Sie eine andere Eigenschaft als erforderlich definieren. Hier ist der Code

public class RequiredIfAttribute : RequiredAttribute
{
    private String PropertyName { get; set; }
    private Object DesiredValue { get; set; }

    public RequiredIfAttribute(String propertyName, Object desiredvalue)
    {
        PropertyName = propertyName;
        DesiredValue = desiredvalue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        Object instance = context.ObjectInstance;
        Type type = instance.GetType();
        Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (proprtyvalue.ToString() == DesiredValue.ToString())
        {
            ValidationResult result = base.IsValid(value, context);
            return result;
        }
        return ValidationResult.Success;
    }
}

Hier ist PropertyName die Eigenschaft, für die Sie Ihre Bedingung festlegen möchten DesiredValue ist der bestimmte Wert von PropertyName (Eigenschaft), für den Ihre andere Eigenschaft für erforderlich validiert werden muss

Sagen Sie, Sie haben folgendes

public class User
{
    public UserType UserType { get; set; }

    [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
    public string Password
    {
        get;
        set;
    }
}

Registrieren Sie zum Schluss den Adapter für Ihr Attribut, damit er die clientseitige Überprüfung durchführen kann (ich habe ihn in global.asax, Application_Start gestellt).

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
33
Dan Hunex

Ich habe dieses erstaunliche Nuget verwendet, das dynamische Anmerkungen macht ExpressiveAnnotations

Sie können jede Logik überprüfen, von der Sie träumen können:

public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }
27
Korayem

Sie können Validatoren bedingt deaktivieren, indem Sie Fehler aus ModelState entfernen:

ModelState["DependentProperty"].Errors.Clear();
17
Pavel Chuchuva

Danke, Merritt :)

Ich habe dies gerade auf MVC 3 aktualisiert, falls es für jemanden nützlich ist: http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp -net-mvc-3.aspx

Simon

8
Simon Ince

Es gibt jetzt ein Framework, das diese bedingte Validierung (neben anderen praktischen Validierungen von Datenanmerkungen) standardmäßig ausführt: http://foolproof.codeplex.com/

Sehen Sie sich insbesondere den Validator [RequiredIfTrue ("IsSenior") an. Sie legen das direkt auf die Eigenschaft, die Sie überprüfen möchten, so dass Sie das gewünschte Verhalten des Validierungsfehlers erhalten, der der Eigenschaft "Senior" zugeordnet ist.

Es ist als NuGet-Paket erhältlich.

6
bojingo

Ich hatte das gleiche Problem, brauchte eine Änderung des Attributs [Required] - Make-Feld in Abhängigkeit von der HTTP-Anfrage erforderlich. Die Lösung war der Antwort von Dan Hunex ähnlich, aber seine Lösung funktionierte nicht korrekt (siehe Kommentare). Ich verwende keine unauffällige Validierung, sondern nur MicrosoftMvcValidation.js aus der Box .. Hier ist es. Implementieren Sie Ihr benutzerdefiniertes Attribut:

public class RequiredIfAttribute : RequiredAttribute
{

    public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
    {

    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {

    //You can put your logic here   

        return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
    }


}

Dann müssen Sie Ihren benutzerdefinierten Anbieter implementieren, um ihn als Adapter in Ihrer global.asax zu verwenden

public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
{

    ControllerContext ccontext;
    public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
       : base(metadata, context, attribute)
    {
        ccontext = context;// I need only http request
    }

//override it for custom client-side validation 
     public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
     {       
               //here you can customize it as you want
         ModelClientValidationRule rule = new ModelClientValidationRule()
         {
             ErrorMessage = ErrorMessage,
    //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"    
             ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
         };
         return new ModelClientValidationRule[] { rule };
      }
}

Und ändern Sie Ihre global.asax mit einer Zeile

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));

und hier ist es

[RequiredIf]
public string NomenclatureId { get; set; }

Der Hauptvorteil für mich ist, dass ich keinen benutzerdefinierten Client-Validator wie bei einer unauffälligen Validierung codieren muss. es funktioniert genauso wie [Required], aber nur in den Fällen, die Sie möchten.

3
Den

Sie müssen auf Personenebene, nicht auf Senior-Ebene validieren, oder Senior muss einen Verweis auf die übergeordnete Person haben. Es scheint mir, dass Sie einen Selbstvalidierungsmechanismus benötigen, der die Validierung für die Person und nicht für eine ihrer Eigenschaften definiert. Ich bin nicht sicher, aber DataAnnotations unterstützt dies nicht sofort. Was Sie tun können, erstellen Sie Ihre eigene Attribute, die von ValidationAttribute abgeleitet ist und auf Klassenebene dekoriert werden kann. Als Nächstes erstellen Sie einen benutzerdefinierten Validator, der die Ausführung dieser Validatoren auf Klassenebene ermöglicht.

Ich weiß, dass Validation Application Block die sofortige Selbstvalidierung unterstützt, aber VAB hat eine ziemlich steile Lernkurve. Trotzdem, hier ein Beispiel mit VAB:

[HasSelfValidation]
public class Person
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    [SelfValidation]
    public void ValidateRange(ValidationResults results)
    {
        if (this.IsSenior && this.Senior != null && 
            string.IsNullOrEmpty(this.Senior.Description))
        {
            results.AddResult(new ValidationResult(
                "A senior description is required", 
                this, "", "", null));
        }
    }
}
3
Steven

Schau dir diesen Kerl an:

http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

Ich arbeite gerade durch sein Beispielprojekt.

2
Merritt

Typische Verwendung für die bedingte Fehlerbeseitigung aus dem Modellstatus:

  1. Machen Sie einen bedingten ersten Teil der Controller-Aktion
  2. Führen Sie eine Logik aus, um den Fehler von ModelState zu entfernen
  3. Führen Sie den Rest der vorhandenen Logik durch (normalerweise die Überprüfung des Modellstatus, dann alles andere).

Beispiel:

public ActionResult MyAction(MyViewModel vm)
{
    // perform conditional test
    // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")

    // Do typical model state validation, inside following if:
    //     if (!ModelState.IsValid)

    // Do rest of logic (e.g. fetching, saving

Behalten Sie in Ihrem Beispiel alles bei, und fügen Sie der Aktion Ihres Controllers die vorgeschlagene Logik hinzu. Ich gehe davon aus, dass Ihr ViewModel, das an die Controller-Aktion übergeben wurde, die Objekte Person und Senior Person mit Daten enthält, die von der Benutzeroberfläche aus mit Daten gefüllt werden.

0

Ich verwende MVC 5, aber Sie könnten so etwas versuchen:

public DateTime JobStart { get; set; }

[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
[DisplayName("Start Date")]
[Required]
public DateTime? StartDate { get; set; }

In Ihrem Fall würden Sie etwas wie "IsSenior == true" ..__ sagen. Dann müssen Sie nur die Gültigkeitsprüfung Ihrer Post-Aktion überprüfen.

0
fosterImposter