Asp.net-Mvc

MVC3 驗證 - 需要組中的一個

  • August 30, 2011

給定以下視圖模型:

public class SomeViewModel
{
 public bool IsA { get; set; }
 public bool IsB { get; set; }
 public bool IsC { get; set; } 
 //... other properties
}

我希望創建一個自定義屬性來驗證至少一個可用屬性是否為真。我設想能夠將屬性附加到屬性並分配一個組名,如下所示:

public class SomeViewModel
{
 [RequireAtLeastOneOfGroup("Group1")]
 public bool IsA { get; set; }

 [RequireAtLeastOneOfGroup("Group1")]
 public bool IsB { get; set; }

 [RequireAtLeastOneOfGroup("Group1")]
 public bool IsC { get; set; } 

 //... other properties

 [RequireAtLeastOneOfGroup("Group2")]
 public bool IsY { get; set; }

 [RequireAtLeastOneOfGroup("Group2")]
 public bool IsZ { get; set; }
}

我想在表單送出之前在客戶端進行驗證,因為表單中的值發生了變化,這就是為什麼我更願意盡可能避免使用類級屬性的原因。

這將需要伺服器端和客戶端驗證來定位具有作為自定義屬性參數傳入的相同組名值的所有屬性。這可能嗎?非常感謝任何指導。

這是一種繼續進行的方法(還有其他方法,我只是說明一種與您的視圖模型相匹配的方法):

[AttributeUsage(AttributeTargets.Property)]
public class RequireAtLeastOneOfGroupAttribute: ValidationAttribute, IClientValidatable
{
   public RequireAtLeastOneOfGroupAttribute(string groupName)
   {
       ErrorMessage = string.Format("You must select at least one value from group \"{0}\"", groupName);
       GroupName = groupName;
   }

   public string GroupName { get; private set; }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext)
   {
       foreach (var property in GetGroupProperties(validationContext.ObjectType))
       {
           var propertyValue = (bool)property.GetValue(validationContext.ObjectInstance, null);
           if (propertyValue)
           {
               // at least one property is true in this group => the model is valid
               return null;
           }
       }
       return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
   }

   private IEnumerable<PropertyInfo> GetGroupProperties(Type type)
   {
       return
           from property in type.GetProperties()
           where property.PropertyType == typeof(bool)
           let attributes = property.GetCustomAttributes(typeof(RequireAtLeastOneOfGroupAttribute), false).OfType<RequireAtLeastOneOfGroupAttribute>()
           where attributes.Count() > 0
           from attribute in attributes
           where attribute.GroupName == GroupName
           select property;
   }

   public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
   {
       var groupProperties = GetGroupProperties(metadata.ContainerType).Select(p => p.Name);
       var rule = new ModelClientValidationRule
       {
           ErrorMessage = this.ErrorMessage
       };
       rule.ValidationType = string.Format("group", GroupName.ToLower());
       rule.ValidationParameters["propertynames"] = string.Join(",", groupProperties);
       yield return rule;
   }
}

現在,讓我們定義一個控制器:

public class HomeController : Controller
{
   public ActionResult Index()
   {
       var model = new SomeViewModel();
       return View(model);        
   }

   [HttpPost]
   public ActionResult Index(SomeViewModel model)
   {
       return View(model);
   }
}

和一個觀點:

@model SomeViewModel

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>

@using (Html.BeginForm())
{
   @Html.EditorFor(x => x.IsA)
   @Html.ValidationMessageFor(x => x.IsA)
   <br/>
   @Html.EditorFor(x => x.IsB)<br/>
   @Html.EditorFor(x => x.IsC)<br/>

   @Html.EditorFor(x => x.IsY)
   @Html.ValidationMessageFor(x => x.IsY)
   <br/>
   @Html.EditorFor(x => x.IsZ)<br/>
   <input type="submit" value="OK" />
}

剩下的最後一部分是為客戶端驗證註冊適配器:

jQuery.validator.unobtrusive.adapters.add(
   'group', 
   [ 'propertynames' ],
   function (options) {
       options.rules['group'] = options.params;
       options.messages['group'] = options.message;
   }
);

jQuery.validator.addMethod('group', function (value, element, params) {
   var properties = params.propertynames.split(',');
   var isValid = false;
   for (var i = 0; i < properties.length; i++) {
       var property = properties[i];
       if ($('#' + property).is(':checked')) {
           isValid = true;
           break;
       }
   }
   return isValid;
}, '');

根據您的具體要求,可能會調整程式碼。

引用自:https://stackoverflow.com/questions/7247748