【问题标题】:ModelMetadata TemplateHint always nullModelMetadata TemplateHint 始终为空
【发布时间】:2013-04-24 19:45:37
【问题描述】:

我正在尝试创建一个自定义 ModelMetadataProvider 来为 JQuery UI 自动完成小部件提供不显眼的属性。

我有一个如下所示的自定义属性:

[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; }
}

当我在视图中使用这个 viewModel 时,我检查了生成的 html,我得到了一个 <input> 标签,它的属性如下:data-autocomplete-url="UrlPlaceholder"。

我接下来要做的是能够在我的视图中指定使用我的 viewModel 的 URL,如下所示:

@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,并使用这行代码ModelMetadataProviders.Current = new CustomModelMetadataProvider();在global.asax中注册了它。

我想要做的是将要由 JQuery UI 自动完成小部件使用的 URL 插入到 metadata.AdditionalValues 字典中以供自动完成编辑器模板使用。

我的自定义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 属性将被设置,因为我将 CreateMetadata 的基本实现称为 DataAnnotationsModelMetadataProvider。

以下是我可以确认的:

  • CustomModelMetadataProvider 已正确注册,因为它包含正在调用的其他代码。
  • 由于生成的 Html 包含一个名为 "data-autocomplete-url" 的属性,因此正在拾取正确的编辑器模板。
  • 如果我在自动完成模板中放置断点,Visual Studio 会转到调试器。

那么任何人都可以帮我解释一下吗?我对ModelMetadataProvider系统有什么误解?

【问题讨论】:

    标签: c# asp.net-mvc-3 data-annotations modelmetadata modelmetadataprovider


    【解决方案1】:

    查看 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-31
      • 2019-07-18
      • 2019-08-18
      • 2011-12-29
      • 2019-07-02
      • 2019-05-04
      • 1970-01-01
      • 2011-05-04
      相关资源
      最近更新 更多