3

ModelMetadataProviderJQuery UI Autocomplete ウィジェットに目立たない属性を提供するカスタムを作成しようとしています。

次のようなカスタム属性があります。

[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ようなタイプのプロパティを持つviewModelがあります。

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このコード行を使用して global.asax に登録しましたModelMetadataProviders.Current = new CustomModelMetadataProvider();

私がやりたいことは、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プロパティが設定されると思っていたでしょう。CreateMetadataDataAnnotationsModelMetadataProvider

確認できることは次のとおりです。

  • CustomModelMetadataProviderは、呼び出される他のコードが含まれているため、正しく登録されています。
  • 生成される Html に という属性が含まれているため、正しいエディター テンプレートが取得されます"data-autocomplete-url"
  • オートコンプリート テンプレートにブレークポイントを配置すると、Visual Studio はデバッガーに移動します。

だから、誰かが私のためにこれに光を当てることができますか? ModelMetadataProviderシステムについて誤解していることは何ですか?

4

1 に答える 1

3

ASP.NET MVC 3 ソース コードを調べたところ、この理由は、モデルに適用される属性のメソッドCreateMetadataより前にメソッドが呼び出されるためであることがわかりました。OnMetadataCreatedIMetadataAware

やりたいことを実行できる代替ソリューションを見つけました。

まず第一に、私は自分を更新しました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)
于 2013-04-17T20:01:11.507 に答える