2013-08-07 71 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