2012-04-20 78 views
0

在完成MVC 2 NerdDinner tutorial的工作时,我看到可以将某些业务逻辑(必填字段,最大长度等)添加到模型上的各个属性。向模型添加自定义业务逻辑验证

如何添加更复杂的验证规则?例如,需要填充两个属性中的一个?

实施例[量子力学/型号/ Particle.cs]:

namespace QuantumMechanics.Models 
{ 
    [MetadataType(typeof(Particle_Validation))] 
    public partial class Particle { 
    } 
    public class Particle_Validation 
    { 
     // Mass is required; easy enough. 
     [Required(ErrorMessage="Mass is required.")] 
     public Mass double {get; set; } 

     // How do I require exactly one or the other? 
     public Position double {get; set; } 
     public Momentum double {get; set; } 
    } 
} 

回答

-1

想到我会发布我的解决方案。 Spencerooni说,没有一种优雅的机制可以为模型本身添加复杂的验证,但这似乎奏效。它利用DefaultModelBinder接口的OnModelUpdated方法覆盖,在底层模型更新时使其无效。

请注意,模型属性上的DataAnnotations仅在绑定字段被重新发布时调用,这意味着验证将根据Mass传递一个没有字段的表单。

的Global.asax

型号/ ParticleModelBinder.cs

public class ParticleModelBinder : DefaultModelBinder 
{ 
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     base.OnModelUpdated(controllerContext, bindingContext); 

     var particle = (Particle)bindingContext.Model; 

     if (<Test for business rule violation here>) 
     { 
      var modelState = bindingContext.ModelState; 
      // Message to appear in validation summary. 
      modelState.AddModelError("","Please enter position OR momentum."); 
      // Messages also appear in summary, but highlight the bound controls too. 
      modelState.AddModelError(bindingContext.ModelName + ".Position", 
       "Please enter position (or momemtum)."); 
      modelState.AddModelError(bindingContext.ModelName + ".Momentum", 
       "Please enter momentum (or position)."); 
     } 
    } 
} 
0

MVC2一旦值已被发布到控制器,你将执行检查。

[HttpPost] 
public ActionResult Add(Particle particleToAdd) 
{ 
    ValidateModel(particleToAdd); 

    if(ModelState.IsValid) 
    { 
     // Add particle 
     return RedirectToAction(...) // etc 
    } 

    // Return the view with our errors 
    return View(particleToAdd); 
} 

// This validate method can be invoked from your Add and Edit actions 
private void ValidateModel(Particle particleToAddOrUpdate) 
{ 
    if(particleToAddOrUpdate.Position == null && particleToAddOrUpdate.Momentum == null) 
    { 
     ModelState.AddModelError("", "You must supply a value for either Position or Momentum"); 
    } 
} 

你可以在错误增加到,如果你喜欢(位置或动量)的特性之一,但我只是将其添加到将出现在您的验证总结的一般错误列表。

<%: Html.ValidationSummary() %> 

MVC3有一个接口IValidateObject哪个抽象该问题的位。它允许您执行像您为每个类指定的检查,即您的粒子对象可以检查绑定时的属性,并且您不必在Add方法中编写检查。

下面是它的用法example

不幸的是,在MVC2,让您在MVC3IValidateObject接口绑定验证多个属性的一类没有开箱。您只需要确保在调用AddEdit操作时调用其他验证方法。

+0

谢谢您的快速回复。这是一种可行的方法,但它仅适用于控制器的Add方法。不应该有一种方法来在模型级别强制执行它,所以它可以由所有控制器方法调用,例如编辑? – AggieEngineer2k 2012-04-20 20:47:29

+0

我已经更新了我的答案,它显示了您需要的附加验证的一些基本重用。不幸的是,我不认为你可以在MVC2中做更多的事情,这就是为什么MVC3添加了IValidateObject接口。 – 2012-04-20 21:52:05

0

这听起来像你的目标的是一个 “必要条件” 的规则,即标记为一个属性要求根据另一个财产的价值。这对于ValidationAttribute来说是不可能的,因为该属性的范围仅限于它所修饰的属性。您需要实现一个匹配的DataAnnotationsModelValidator以具有更广的范围(如果您希望它还验证客户端,则需要使用与关联的客户端JavaScript相关的ModelClientValidationRule)。

所以,你ConditionalRequiredAttribute看起来是这样的:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] 
public class ConditionalRequiredAttribute : ValidationAttribute 
{ 
    public ConditionalRequiredAttribute(string triggerProperty, object triggerValue) 
    { 
     TriggerProperty = triggerProperty; 
     TriggerValue = triggerValue; 
    } 

    /// <summary> 
    /// Checks that the value of the decorated member is not null/empty if the TriggerProperty's value is equal to TriggerValue. 
    /// </summary> 
    /// <param name="value">The data field value to validate.</param> 
    /// <returns>true if validation is successful; otherwise, false.</returns> 
    public override bool IsValid(object value) 
    { 
     if (value == null) 
      return false; 

     if (value is double) 
     { 
      return !((double)value).Equals(0); 
     } 

     string s = value as string; 
     if (s != null) 
      return s.Length > 0; 

     return true; 
    } 

    /// <summary> 
    /// The name of the property whose value will be checked to trigger the required field 
    /// </summary> 
    public string TriggerProperty { get; set; } 

    /// <summary> 
    /// The expected value of the trigger property that will trigger the required field 
    /// </summary> 
    public object TriggerValue { get; set; } 
} 

这实际上是相当多的标准Required属性相同 - “特殊酱汁”,实际上是验证,您可以使用它只能调用IsValid方法在某些情况下(即TriggerProperty.Value == TriggerValue)。校验器看起来像:

public class ConditionalRequiredValidator : DataAnnotationsModelValidator<ConditionalRequiredAttribute> 
{ 
    public ConditionalRequiredValidator(ModelMetadata metadata, ControllerContext context, 
             ConditionalRequiredAttribute attribute) 
     : base(metadata, context, attribute) 
    { 

    } 


    /// <summary> 
    /// Override the default validate method to only execute if the TriggerProperty's value is equal to TriggerValue 
    /// </summary> 
    public override IEnumerable<ModelValidationResult> Validate(object container) 
    { 
     // Does the specified property exist in the metadata? 
     PropertyInfo triggerProperty = Metadata.ContainerType.GetProperty(Attribute.TriggerProperty); 
     if (triggerProperty != null) 
     { 
      object actualValue = triggerProperty.GetValue(container, null); 
      if (actualValue != null) 
      { 
       if (Attribute.TriggerValue.Equals(actualValue)) 
       { 
        // Run IsValid for the property if the actual value matches the expected value 
        foreach (ModelValidationResult result in base.Validate(container)) 
        { 
         yield return result; 
        } 

       } 
      } 
     } 
    } 
} 

最后,你需要与供应商注册ConditionalRequiredValidator以确保框架倾向于采用这样一种ConditionalRequiredAttribute时。要做到这一点,添加以下行的Application_Start()方法Global.asax.cs中的:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ConditionalRequiredAttribute), typeof(ConditionalRequiredValidator)); 

然后你装饰颗粒类的成员(或任何其他应用类)就像这样:

public class Particle 
{ 
    // Mass is required; easy enough. 
    [Required(ErrorMessage="Mass is required.")] 
    public double Mass { get; set; } 

    [ConditionalRequired("Momentum", 0D, ErrorMessage = "Position must be set if Momentum is not.")] 
    public double Position { get; set; } 

    [ConditionalRequired("Position", 0D, ErrorMessage = "Momentum must be set if Position is not.")] 
    public double Momentum { get; set; } 
} 

Voila,你现在应该能够根据另一个字段的值有条件地验证一个字段。

在一个旁注,你可以抽象此条件逻辑成一个辅助类,并创建了一系列的“有条件”的验证,ConditionalRequired,ConditionalRange等等...

注2:虽然它可能更复杂/“更多的代码“比你自己的解决方案(当我还在一起回复时发布的内容 - doh!),这确实具有可重用的好处。要为未来的视图模型添加相同的功能,您只需用ConditionalRequired属性修饰您的属性...