ModelMetadata TemplateHint всегда нулевой

Я пытаюсь создать кастом 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)

У меня есть viewModel со свойством типа string это включает в себя AutocompleteAttribute как это:

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

Когда я использую эту модель представления в моем представлении, я проверяю сгенерированный HTML, и я получаю <input> тег с таким атрибутом: data-autocomplete-url="UrlPlaceholder",

Далее я хочу указать URL-адрес в моем представлении, который использует мою 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();,

Я хочу вставить URL-адрес, который будет использоваться виджетом автозаполнения пользовательского интерфейса JQuery, в 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 система?

1 ответ

Решение

Изучив исходный код ASP.NET MVC 3, я обнаружил, что причина этого в том, что CreateMetadata метод вызывается до OnMetadataCreated метод любого IMetadataAware атрибуты, которые применяются к модели.

Я нашел альтернативное решение, которое позволяет мне делать то, что я хотел.

Прежде всего, я обновил свой 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);
        }
    }
}

и мой вспомогательный метод Html для установки URL в моих представлениях выглядит так:

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)
Другие вопросы по тегам