3

我正在嘗試創建自定義ModelMetadataProvider以爲JQuery UI自動填充小部件提供不顯眼的屬性。ModelMetadata TemplateHint始終爲空

我有一個看起來像這樣的自定義屬性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public sealed class AutocompleteAttribute : Attribute, IMetadataAware 
{ 
    public void OnMetadataCreated(ModelMetadata metadata) 
    { 
     metadata.TemplateHint = "Autocomplete"; 
    } 
} 

和看起來像這樣的編輯模板:

@{ 
    var attributes = new RouteValueDictionary 
    { 
     {"class", "text-box single-line"}, 
     {"autocomplete", "off"}, 
     {"data-autocomplete-url", "UrlPlaceholder" }, 
    }; 
} 

@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes) 

我有一個視圖模型與包括string類型的屬性AutocompleteAttribute是這樣的:

public class MyViewModel 
{ 
    [Autocomplete] 
    public string MyProperty { get; set; } 
} 

當我用th在我看來viewModel我檢查生成的HTML,我得到一個<input>標記,它具有這樣的屬性:data-autocomplete-url="UrlPlaceholder"

我想接下來做的是能夠指定在我看來,使用我的ViewModel這樣的網址:

@model MyViewModel 

@{ ViewBag.Title = "Create item"; } 

@Html.AutoCompleteUrlFor(p => p.MyProperty, UrlHelper.GenerateUrl(null, "Autocomplete", "Home", null, Html.RouteCollection, Html.ViewContext.RequestContext, true)) 

// Other stuff here... 

<div> 
    @Html.ActionLink("Back to List", "Index") 
</div> 

AutoCompleteForUrl幫手只是保存生成的URL在字典中,使用屬性名稱作爲一個關鍵。

接下來我創建了一個自定義ModelMetadataProvider並在global.asax中使用這行代碼ModelMetadataProviders.Current = new CustomModelMetadataProvider();進行了註冊。

我想要做的就是將由JQuery UI Autocomplete小部件使用的URL插入metadata.AdditionalValues字典中,以供Autocomplete編輯器模板使用。

我定製ModelMetadataProvider看起來是這樣的:

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider 
{ 
    protected override ModelMetadata CreateMetadata(IEnumerable<System.Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 
    { 
     var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); 

     if (metadata.TemplateHint == "Autocomplete") 
     { 
      string url; 
      if(htmlHelpers.AutocompleteUrls.TryGetValue(metadata.propertyName, out url) 
      { 
       metadata.AdditionalValues["AutocompleteUrl"] = url; 
      } 
     } 

     return metadata; 
    } 
} 

和我更新的編輯模板看起來是這樣的:

@{ 
    object url; 

    if (!ViewContext.ViewData.ModelMetadata.TryGetValue("AutocompleteUrl", out url)) 
    { 
     url = ""; 
    } 

    var attributes = new RouteValueDictionary 
    { 
     {"class", "text-box single-line"}, 
     {"autocomplete", "off"}, 
     {"data-autocomplete-url", (string)url }, 
    }; 
} 

@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes) 

的問題是,在TemplateHint財產總是不等於「自動完成」我的自定義模型元數據提供程序,所以我的邏輯來生成URL永遠不會被調用。我會認爲在這一點上,TemplateHint屬性將被設置爲我所稱的DataAnnotationsModelMetadataProviderCreateMetadata的基本實現。

這是我可以證實:

  • CustomModelMetadataProvider正確註冊,因爲它包含了獲取調用其他代碼。
  • 由於生成的Html包含一個名爲"data-autocomplete-url"的屬性,正確的編輯器模板正在被拾取。
  • 如果我在自動填充模板中放置了斷點,Visual Studio將轉到調試器。

所以,任何人都可以爲我擺脫這個燈嗎?我對ModelMetadataProvider系統有什麼誤解?

回答

3

通過ASP查看後。NET MVC 3源代碼我發現原因是因爲CreateMetadata方法在應用於模型的任何IMetadataAware屬性的OnMetadataCreated方法之前被調用。

我發現了另一種解決方案,可以讓我做我想做的事。

所有我更新首先我AutocompleteAttribute

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public class AutocompleteAttribute : Attribute, IMetadataAware 
{ 
    public const string Key = "autocomplete-url"; 
    internal static IDictionary<string, string> Urls { get; private set; } 

    static AutocompleteAttribute() 
    { 
     Urls = new Dictionary<string, string>(); 
    } 

    public void OnMetadataCreated(ModelMetadata metadata) 
    { 
     metadata.TemplateHint = "Autocomplete"; 

     string url; 

     if (Urls.TryGetValue(metadata.PropertyName, out url)) 
     { 
      metadata.AdditionalValues[Key] = url; 
      Urls.Remove(metadata.PropertyName); 
     } 
    } 
} 

,並在我的意見設置的URL我的HTML輔助方法是這樣的:

public static IHtmlString AutocompleteUrlFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string url) 
{ 
    if (string.IsNullOrEmpty(url)) 
     throw new ArgumentException("url"); 

    var property = ModelMetadata.FromLambdaExpression(expression, html.ViewData).PropertyName; 
    AutocompleteAttribute.Urls[property] = url; 

    return MvcHtmlString.Empty; 
} 

然後我必須做我的編輯模板是這樣的:

@{ 
    object url; 
    ViewData.ModelMetadata.AdditionalValues.TryGetValue(AutocompleteAttribute.Key, out url); 

    var attributes = new RouteValueDictionary 
    { 
     {"class", "text-box single-line"}, 
     {"autocomplete", "off"}, 
     { "data-autocomplete-url", url }, 
    }; 
} 

@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes)