ASP.NET MVC Koşullu doğrulama


129

Model üzerinde koşullu doğrulama yapmak için veri açıklamaları nasıl kullanılır?

Örneğin, aşağıdaki modele sahip olduğumuzu varsayalım (Kişi ve Yaşlı):

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;
    }
}

Ve aşağıdaki görünüm:

<%= 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)%>

"IsSenior" özelliğinin seçimine dayalı olarak "Senior.Description" özelliği koşullu zorunlu alan olmak istiyorum (true -> gerekli). Veri ek açıklamalarıyla ASP.NET MVC 2'de koşullu doğrulama nasıl uygulanır?


1
Geçenlerde benzer bir soru sordum: stackoverflow.com/questions/2280539/…
Darin Dimitrov

Kafam karıştı. Bir Seniornesne her zaman kıdemli bir nesnedir, öyleyse bu durumda IsSenior neden yanlış olabilir? Person.IsSeniorFalse olduğunda sadece 'Person.Senior' özelliğinin boş olması gerekmez mi ? Ya da IsSeniorözelliği neden aşağıdaki gibi uygulamıyorsunuz bool IsSenior { get { return this.Senior != null; } }?
Steven

Steven: "IsSenior", görünümdeki onay kutusu alanına çevrilir. Kullanıcı "IsSenior" onay kutusunu işaretlediğinde, "Senior.Description" Alanı zorunlu hale gelir.
Peter Stegnar

Darin Dimitrov: Pekala, ama tam olarak değil. Görüyorsunuz, hata mesajının belirli bir alanda görünmesini nasıl başarırsınız? Nesne düzeyinde doğrulama yaparsanız, nesne düzeyinde bir hata alırsınız. Mülk düzeyinde hataya ihtiyacım var.
Peter Stegnar

Yanıtlar:


150

MVC3'e koşullu doğrulama kuralları eklemenin çok daha iyi bir yolu var; modelinizin yöntemi devralmasını IValidatableObjectve uygulamasını sağlayın Validate:

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.");
    }
}

ASP.NET MVC 3'e Giriş (Önizleme 1) bölümünde daha fazlasını okuyun .


özellik "int" türüyse, bu değer gerektirir, bu alanı doldurursanız, Doğrulama çalışmaz ..
Jeyhun Rahimov

2
Ne yazık ki, Microsoft bunu yanlış katmana koydu - doğrulama iş mantığıdır ve bu arabirim System.Web DLL dosyasındadır. Bunu kullanmak için, iş katmanınıza bir sunum teknolojisine bağımlılık vermelisiniz.
NightOwl888


4
falconwebtech.com/post/… - @viperguynaz bu çalışmıyor
Smit Patel

1
@RayLoveless aramalısınız ModelState.IsValid- doğrudan Validate'i aramayın
viperguynaz

63

Bunu denetleyicinin içerdiği "ModelState" sözlüğünü kullanarak çözdüm . ModelState sözlüğü, doğrulanması gereken tüm üyeleri içerir.

İşte çözüm:

Bazı alanlara dayalı bir koşullu doğrulama uygulamanız gerekiyorsa (örneğin, eğer A = doğru ise, o zaman B gereklidir), özellik seviyesinde hata mesajlarını korurken (bu, nesne seviyesindeki özel doğrulayıcılar için doğru değildir) bunu başarabilirsiniz. "ModelState" i işleyerek, sadece istenmeyen doğrulamaları ondan kaldırarak.

... Bazı sınıflarda ...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

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

... ders devam ediyor ...

... Bazı kontrolör eylemlerinde ...

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

...

Bununla, her şeyi aynı bırakırken, koşullu doğrulama elde ederiz.


GÜNCELLEME:

Bu benim son uygulamam: Model üzerinde bir arayüz ve söz konusu arayüzü uygulayan modeli doğrulayan eylem niteliği kullandım. Arayüz, Validate (ModelStateDictionary modelState) yöntemini tanımlar. Eylemdeki öznitelik IValidatorSomething'de Validate (modelState) 'i çağırır.

Bu cevabı karmaşıklaştırmak istemedim, bu yüzden nihai uygulama ayrıntılarından bahsetmedim (sonunda, üretim kodunda önemlidir).


17
Bunun dezavantajı, doğrulama mantığınızın bir kısmının modelde ve diğer kısmının kontrolör (ler) de bulunmasıdır.
Kristof Claes

Tabii ki bu gerekli değil. Sadece en temel örneği gösteriyorum. Bunu, model üzerindeki arayüzle ve belirtilen arayüzü uygulayan modeli doğrulayan eylem özniteliğiyle uyguladım. Arayüz, Validate (ModelStateDictionary modelState) yöntemine bakar. Sonunda modeldeki tüm doğrulamaları yaparsınız. Neyse, iyi nokta.
Peter Stegnar

MVC ekibi kutudan daha iyi bir şey inşa edene kadar bu yaklaşımın basitliğini seviyorum. Ancak çözümünüz istemci tarafı doğrulaması etkinken çalışıyor mu?
Aaron

2
@Aaron: Çözümü beğendiğiniz için mutluyum, ancak ne yazık ki bu çözüm istemci tarafı doğrulamayla çalışmıyor (çünkü her doğrulama özniteliğinin JavaScript uygulamasına ihtiyacı var). "Uzak" özniteliğiyle kendinize yardımcı olabilirsiniz, bu nedenle onu doğrulamak için yalnızca Ajax çağrısı gönderilecektir.
Peter Stegnar

Bu cevabı genişletebilir misin? Bu biraz mantıklı ama bunda kristal olduğumdan emin olmak istiyorum. Tam olarak bu durumla karşı karşıyayım ve çözülmesini istiyorum.
Richard B

36

Dün aynı sorunu yaşadım ama hem istemci tarafı hem de sunucu tarafı doğrulama için çalışan çok temiz bir şekilde yaptım.

Koşul: Modeldeki diğer özelliğin değerine bağlı olarak, başka bir özelliği gerekli kılmak istiyorsunuz. İşte kod

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;
    }
}

Burada PropertyName, koşulunuzu yapmak istediğiniz özelliktir. DesiredValue, diğer mülkünüzün gerekli olduğu için doğrulanması gereken ÖzellikAdı'nın (özellik) belirli değeridir.

Aşağıdakilere sahip olduğunuzu söyleyin

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

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

Sonunda, ama en az değil, istemci tarafında doğrulama yapabilmesi için özniteliğiniz için bağdaştırıcı kaydedin (bunu global.asax, Application_Start içine koydum)

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

Bu, orijinal başlangıç ​​noktasıdır miroprocessordev.blogspot.com/2012/08/…
Dan Hunex

Asp.net mvc2'de eşdeğer bir çözüm var mı? ValidationResult, ValidationContext sınıfları asp.net mvc2'de (.net framework 3.5) kullanılamaz
User_MVC

2
Bu yalnızca bağlantılı blogda belirtildiği gibi sunucu tarafında çalışır
Pakman

2
Bunu istemci tarafında MVC5 ile çalıştırmayı başardım, ancak istemcide DesiredValue ne olursa olsun doğrulamayı ateşliyor.
Geethanga

1
@Dan Hunex: MVC4'te, istemci tarafında düzgün çalışmayı başaramadım ve DesiredValue ne olursa olsun doğrulamayı başlatıyor. Herhangi bir yardım pls?
Jack

34

Dinamik ek açıklamalar yapan bu şaşırtıcı Nuget kullanıyorum ExpressiveAnnotations

Hayal edebileceğiniz herhangi bir mantığı doğrulayabilirsiniz:

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

3
ExpressiveAnnotation kitaplığı, buradaki tüm yanıtlar arasında en esnek ve genel çözümdür. Paylaşım için teşekkürler!
Sudhanshu Mishra

2
Sağlam bir gün boyunca bir çözüm bulmaya çalışırken kafamı yumrukluyorum. ExpressiveAnnotations benim için çözüm gibi görünüyor!
Caverman

ExpressiveAnnotation kitaplığı harika!
Doug Knudsen

1
Müşteri tarafı desteği de var!
Nattrass

1
Yine de .NET Core desteği yok ve gerçekleşecek gibi görünmüyor.
gosr

18

Doğrulayıcıları, ModelState'ten hataları kaldırarak koşullu olarak devre dışı bırakabilirsiniz:

ModelState["DependentProperty"].Errors.Clear();


6

Artık bu koşullu doğrulamayı (diğer kullanışlı veri açıklama doğrulamalarının yanı sıra) kutudan çıkaran bir çerçeve var: http://foolproof.codeplex.com/

Özellikle, [RequiredIfTrue ("IsSenior")] doğrulayıcıya bir göz atın. Bunu doğrudan doğrulamak istediğiniz mülke koyarsınız, böylece "Senior" mülkü ile ilişkili doğrulama hatasının istenen davranışını elde edersiniz.

NuGet paketi olarak mevcuttur.


3

Üst Düzeyde değil, Kişi düzeyinde doğrulamanız gerekir, aksi takdirde Kıdemli, ebeveyn Kişisine referans olmalıdır. Bana öyle geliyor ki, kendi özelliklerinden birinde değil, Kişi üzerinde doğrulamayı tanımlayan bir kendi kendini doğrulama mekanizmasına ihtiyacınız var. Emin değilim, ancak DataAnnotations'ın bunu kutudan çıkardığını sanmıyorum. Ne yapabilirsin kendininkini yaratAttribute ki kendininkiniValidationAttributeSınıf düzeyinde dekore edilebilir ve daha sonra bu sınıf düzeyindeki doğrulayıcıların çalışmasına da olanak tanıyan özel bir doğrulayıcı oluşturabilirsiniz.

Doğrulama Uygulama Bloğunun kullanıma hazır kendi kendini doğrulamayı desteklediğini biliyorum, ancak VAB'nin oldukça dik bir öğrenme eğrisi var. Yine de, işte VAB kullanan bir örnek:

[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));
        }
    }
}

"Kıdemli düzeyinde değil, Kişi düzeyinde doğrulama yapmanız gerekir" Evet, bu bir seçenektir, ancak Hatanın, Kıdemli nesnede gerekli olan belirli bir alana eklenmesi yeteneğini kaybedersiniz.
Peter Stegnar

3

Aynı sorunu yaşadım, [Gerekli] özniteliğinde bir değişiklik yapmam gerekiyordu - http isteğine bağlı olarak alanı gerekli hale getirin Çözüm, Dan Hunex yanıtına benziyordu, ancak çözümü doğru çalışmadı (yorumlara bakın). Göze batmayan doğrulama kullanmıyorum, kutudan sadece MicrosoftMvcValidation.js kullanıyorum. İşte burada. Özel özelliğinizi uygulayın:

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
    }


}

O zaman global.asax'ınızda bir adaptör olarak kullanmak için özel sağlayıcınızı uygulamanız gerekir.

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 };
      }
}

Ve global.asax'ınızı bir çizgi ile değiştirin

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

ve işte burada

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

Benim için temel avantaj, göze batmayan doğrulama durumunda olduğu gibi özel istemci doğrulayıcısını kodlamam gerekmemesidir. aynı [Gerekli] olarak çalışır, ancak yalnızca istediğiniz durumlarda çalışır.


Genişletmeyle ilgili kısım DataAnnotationsModelValidatortam olarak görmem gereken şeydi. Teşekkür ederim.
twip


0

Hatanın Model Durumundan koşullu kaldırılması için tipik kullanım:

  1. Denetleyici eyleminin koşullu ilk bölümünü yapın
  2. ModelState'ten hatayı kaldırmak için mantık gerçekleştirin
  3. Mevcut mantığın geri kalanını yapın (tipik olarak Model Durum doğrulaması, sonra diğer her şey)

Misal:

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

Örneğinizde, her şeyi olduğu gibi tutun ve Kontrolörünüzün Eylemine önerilen mantığı ekleyin. ViewModel'inizin denetleyici eylemine geçirildiğini varsayıyorum, Kullanıcı ve Kıdemli Kişi nesnelerine, kullanıcı arayüzünden veri doldurulmuş.


0

MVC 5 kullanıyorum ama bunun gibi bir şey deneyebilirsiniz:

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; }

Sizin durumunuzda "IsSenior == true" gibi bir şey söylersiniz. O zaman sadece gönderim işleminizdeki doğrulamayı kontrol etmeniz gerekir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.