2013-08-07 47 views
0

我已经创建了自定义ModelValidatorProvider,它应该仅在网站中的某些特定操作中处于活动状态。 为了做到这一点,我创建了一个自定义属性,它扩展了“FilterAttribute”,并在OnAuthorize中设置了验证器。ModelValidatorProviders基于页面

在filter属性中 - 为了只在需要的页面设置验证器,我这样做(PostAttributeModelValidatorProvider是验证器提供者)。

 var provider = (from p in ModelValidatorProviders.Providers 
         where p is PostAttributeModelValidatorProvider 
         select p).FirstOrDefault(); 
     if (provider != null) 
     { 
      ModelValidatorProviders.Providers.Remove(provider); 
     } 

     if (EnableAttributesValidation) 
     { 
      ModelValidatorProviders.Providers.Add(new PostAttributeModelValidatorProvider() { BypassRequiredFieldsValidation = this.BypassRequiredFieldsValidation }); 
     } 

我面临的问题是,有时(我无法检测到什么时候出了问题 - 但我认为,当两种用途尝试访问的网站或触发这个动作的页面时)还有就是我在做正因为这样的删除和添加操作之间的冲突 - 我得到一个错误:

Index was outside the bounds of the array.

相关的堆栈跟踪是:

[IndexOutOfRangeException: Index was outside the bounds of the array.] System.Collections.Generic.Enumerator.MoveNext() +112
System.Linq.d_71 1.MoveNext() +643
System.Linq.<SelectManyIterator>d__14
2.MoveNext() +578
System.Linq.d
_14 2.MoveNext() +578
System.Web.Mvc.UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(IEnumerable
1 clientRules, IDictionary 2 results) +440
System.Web.Mvc.HtmlHelper.GetUnobtrusiveValidationAttributes(String name, ModelMetadata metadata) +280
System.Web.Mvc.Html.InputExtensions.InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, String name, Object value, Boolean useViewData, Boolean isChecked, Boolean setId, Boolean isExplicitValue, String format, IDictionary
2 htmlAttributes) +1050
System.Web.Mvc.Html.InputExtensions.TextBoxFor(HtmlHelper 1 htmlHelper, Expression 1 expression, String format, IDictionary`2 htmlAttributes) +202 ASP._Page_Views_Home_Index_cshtml.Execute() in c:\interpub\wwwroot\Views\Home\Index.cshtml:47

共该属性mplete源代码是:

public class InitializePostAttributesResolverAttribute : FilterAttribute, IAuthorizationFilter 
{ 
    public InitializePostAttributesResolverAttribute() 
     : this(true, false) 
    { 

    } 

    public InitializePostAttributesResolverAttribute(bool enableAttributesValidation, bool bypassRequiredFieldsValidation) 
    { 
     this.EnableAttributesValidation = enableAttributesValidation; 
     this.BypassRequiredFieldsValidation = bypassRequiredFieldsValidation; 
    } 

    /// <summary> 
    /// Should the attributes input be validated 
    /// </summary> 
    public bool EnableAttributesValidation { get; set; } 


    /// <summary> 
    /// Gets or sets the value whether we should bypass the required fields validation 
    /// </summary> 
    /// <remarks> 
    /// This value should be set to true only if you would like to skip on required fields validation. 
    /// We should use this value when searching. 
    /// </remarks> 
    public bool BypassRequiredFieldsValidation { get; set; } 

    public virtual void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 

     ModelMetadataProviders.Current = new PostAttributeModelMetadataProvider(); 

     var provider = (from p in ModelValidatorProviders.Providers 
         where p is PostAttributeModelValidatorProvider 
         select p).FirstOrDefault(); 
     if (provider != null) 
     { 
      ModelValidatorProviders.Providers.Remove(provider); 
     } 

     if (EnableAttributesValidation) 
     { 
      ModelValidatorProviders.Providers.Add(new PostAttributeModelValidatorProvider() { BypassRequiredFieldsValidation = this.BypassRequiredFieldsValidation }); 
     } 
    } 
} 

而且样品使用它

// 
    // POST: /Post/Publish/5 

    [InitializePostAttributesResolver] 
    [HttpPost] 
    [ValidateAntiForgeryToken] 
    public ActionResult Publish(PublishViewModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      // ... 
     } 
    } 

难道我这样做对吗?我的目标(只是要明确)是仅在装饰操作中启用验证程序提供程序,而在验证程序提供程序集合中应该不存在其他操作。

谢谢!

回答

1

ModelValidatorProviders.Providers是一个静态属性,所以你不能通过这种方式实现线程安全。

相反,您应该在启动时将自定义的ModelValidatorProvider添加到列表中,但使提供程序依赖于模型上的特定属性或其属性(与DataAnnotation属性的逻辑相同)。

[MyCustomValidation] 
public class MyModel 
{ 
    public string MyProperty { get; set; } 
} 

而这应该做的伎俩。

+0

没有想到,谢谢! (虽然我仍然需要找出一种方法来添加“BypassRequiredFieldsValidation”属性,该属性在搜索时应该为false,在添加/编辑时为true),但我会考虑一种方式)。 – OzB

+0

如果仅搜索,则可能会根据搜索词从数据库中填充模型,因此不会触发验证。验证仅在您使用(默认)ModelBinder(发布请求,或Controller方法UpdateModel(..)和TryUpdateModel(...))时触发 – Nenad

+0

是的我知道 - 我已经获得由DB创建的自定义属性与“FieldType”等)。验证器提供程序验证属性(必需的,日期等)。在搜索中 - 我仍然需要执行验证(例如“日期不大于X”或“0或更多”),但不希望要求值 - 因为与添加或编辑记录时不同 - 在搜索时,它们应该保持可选状态并处于这种情况下,我不包括他们在搜索。 – OzB