簡體   English   中英

DataAnnotations“NotRequired”屬性

[英]DataAnnotations “NotRequired” attribute

我有一種復雜的模型。

我有我的UserViewModel ,它有幾個屬性,其中兩個是HomePhoneWorkPhone 兩種類型的PhoneViewModel PhoneViewModel我有CountryCodeAreaCodeNumber的所有字符串。 我想使CountryCode可選,但AreaCodeNumber必需的。

這非常有效。 我的問題是,在UserViewModel WorkPhone是必需的,而HomePhone則不是。

反正我有可以dissable Require在attributs PhoneViewModel通過設置任何屬性HomeWork財產?

我試過這個:

[ValidateInput(false)]

但它只適用於類和方法。

碼:

public class UserViewModel
{
    [Required]
    public string Name { get; set; }

    public PhoneViewModel HomePhone { get; set; }

    [Required]    
    public PhoneViewModel WorkPhone { get; set; }
}

public class PhoneViewModel
{
    public string CountryCode { get; set; }

    public string AreaCode { get; set; }

    [Required]
    public string Number { get; set; }
}

[更新於2012年5月24日,以使想法更清晰]

我不確定這是正確的方法,但我認為你可以擴展這個概念,並可以創建一個更通用/可重用的方法。

在ASP.NET MVC中,驗證發生在綁定階段。 將表單發布到服務器時, DefaultModelBinder是根據請求信息創建模型實例並將驗證錯誤添加到ModelStateDictionary實例。

在您的情況下,只要綁定發生在HomePhone ,驗證就會啟動, 我認為通過創建自定義驗證屬性或類似方法,我們無法做很多事情。

我正在考慮的是,當表單中沒有可用的值(areacode,countrycode和number或為空)時, HomePhone屬性根本不創建模型實例,當我們控制綁定時,我們控制驗證,為此,我們必須創建自定義模型綁定器

自定義模型綁定器中,我們檢查屬性是否為HomePhone以及表單是否包含其屬性的任何值,如果不是,我們不綁定屬性,並且HomePhone不會進行HomePhone 簡單地說,價值HomePhone將在空UserViewModel

  public class CustomModelBinder : DefaultModelBinder
  {
      protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
      {
        if (propertyDescriptor.Name == "HomePhone")
        {
          var form = controllerContext.HttpContext.Request.Form;

          var countryCode = form["HomePhone.CountryCode"];
          var areaCode = form["HomePhone.AreaCode"];
          var number = form["HomePhone.Number"];

          if (string.IsNullOrEmpty(countryCode) && string.IsNullOrEmpty(areaCode) && string.IsNullOrEmpty(number))
            return;
        }

        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
      }
  }

最后,您必須在global.asax.cs中注冊自定義模型綁定器。

  ModelBinders.Binders.Add(typeof(UserViewModel), new CustomModelBinder());

所以你現在有一個以UserViewModel為參數的動作,

 [HttpPost]
 public Action Post(UserViewModel userViewModel)
 {

 }

我們的自定義模型粘合劑發揮作用,形式不張貼的AREACODE,COUNTRYCODE和號碼的任何值HomePhone ,不會有任何驗證錯誤和userViewModel.HomePhone為空。 如果表單至少發布了這些屬性的任何一個值,那么HomePhone將按預期進行驗證。

我一直在使用這個做動態注釋的神奇的nuget: ExpressiveAnnotations

它允許你做以前無法做到的事情

[AssertThat("ReturnDate >= Today()")]
public DateTime? ReturnDate { get; set; }

甚至

public bool GoAbroad { get; set; }
[RequiredIf("GoAbroad == true")]
public string PassportNumber { get; set; }

更新:在單元測試中編譯注釋以確保不存在錯誤

正如@diego所提到的,在字符串中編寫代碼可能會令人生畏,但以下是我用於單元測試查找編譯錯誤的所有驗證的內容。

namespace UnitTest
{
    public static class ExpressiveAnnotationTestHelpers
    {
        public static IEnumerable<ExpressiveAttribute> CompileExpressiveAttributes(this Type type)
        {
            var properties = type.GetProperties()
                .Where(p => Attribute.IsDefined(p, typeof(ExpressiveAttribute)));
            var attributes = new List<ExpressiveAttribute>();
            foreach (var prop in properties)
            {
                var attribs = prop.GetCustomAttributes<ExpressiveAttribute>().ToList();
                attribs.ForEach(x => x.Compile(prop.DeclaringType));
                attributes.AddRange(attribs);
            }
            return attributes;
        }
    }
    [TestClass]
    public class ExpressiveAnnotationTests
    {
        [TestMethod]
        public void CompileAnnotationsTest()
        {
            // ... or for all assemblies within current domain:
            var compiled = Assembly.Load("NamespaceOfEntitiesWithExpressiveAnnotations").GetTypes()
                .SelectMany(t => t.CompileExpressiveAttributes()).ToList();

            Console.WriteLine($"Total entities using Expressive Annotations: {compiled.Count}");

            foreach (var compileItem in compiled)
            {
                Console.WriteLine($"Expression: {compileItem.Expression}");
            }

            Assert.IsTrue(compiled.Count > 0);
        }


    }
}

我不會選擇modelBinder; 我使用自定義ValidationAttribute:

public class UserViewModel
{
    [Required]
    public string Name { get; set; }

    public HomePhoneViewModel HomePhone { get; set; }

    public WorkPhoneViewModel WorkPhone { get; set; }
}

public class HomePhoneViewModel : PhoneViewModel 
{
}

public class WorkPhoneViewModel : PhoneViewModel 
{
}

public class PhoneViewModel 
{
    public string CountryCode { get; set; }

    public string AreaCode { get; set; }

    [CustomRequiredPhone]
    public string Number { get; set; }
}

然后:

[AttributeUsage(AttributeTargets.Property]
public class CustomRequiredPhone : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        ValidationResult validationResult = null;

        // Check if Model is WorkphoneViewModel, if so, activate validation
        if (validationContext.ObjectInstance.GetType() == typeof(WorkPhoneViewModel)
         && string.IsNullOrWhiteSpace((string)value) == true)
        {
            this.ErrorMessage = "Phone is required";
            validationResult = new ValidationResult(this.ErrorMessage);
        }
        else
        {
            validationResult = ValidationResult.Success;
        }

        return validationResult;
    }
}

如果不清楚,我會提供一個解釋,但我認為這是不言自明的。

只是一些觀察:如果綁定不僅僅是簡單的提交,下面的代碼就會出現問題。 我有一個案例,在對象中有嵌套對象,它會跳過它並且caouse有些文件沒有在嵌套對象中綁定。

可能的解決辦法是

protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
     {
         if (!propertyDescriptor.Attributes.OfType<RequiredAttribute>().Any())
         {
             var form = controllerContext.HttpContext.Request.Form;

             if (form.AllKeys.Where(k => k.StartsWith(string.Format(propertyDescriptor.Name, "."))).Count() > 0)
             {
                 if (form.AllKeys.Where(k => k.StartsWith(string.Format(propertyDescriptor.Name, "."))).All(
                         k => string.IsNullOrWhiteSpace(form[k])))
                     return;
             }
         }

         base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
     }

非常感謝Altaf Khatri

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM