ModelMetadata.FromLambdaExpressionが親のメタデータを参照する

投稿者: Anonymous

ASP.NET MVC2でCustomHelperを作っています。

必要な情報が取得できない問題があったので、
解決方法があればご教示ください。

ViewModelにDisplayName属性やValidationAttributeをつけています。

属性を取得する際はModelMetadata.FromLambdaExpression()で取得できますが、
対象のViewModelがBaseViewModelをoverrideしている場合、
BaseViewModelの属性(たとえばDisplayName)を取得するようです。
※今回の場合、BaseViewModelにはDisplayName属性をつけていないので
 取得できない

    public static MvcHtmlString LabelExFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

        return LabelExFor(html, expression, metadata.IsRequired);
    }

ViewではBaseViewModelをoverrideしたViewModelを用いているので
ViewModelの属性を取得したいのですが、どのようにすればよいでしょうか?

尚、ViewModelのプロパティをoverrideではなくnewで定義した場合は
ViewModelの属性を取ってくるようです。

最終的な回避方法

標準のLabelForは用いず、自作のLabelExForで置き換える。LabelExForではFromLambdaExpression()ではなく、FromStringExpression()を用いてmetadataの取得を行う。

public static MvcHtmlString LabelExFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
{
    var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
    var metadata = ModelMetadata.FromStringExpression(ExpressionHelper.GetExpressionText(expression), html.ViewData);

    string labelText = metadata.DisplayName ?? 
                        metadata.PropertyName ?? 
                        htmlFieldName.Split('.').Last();
    if (String.IsNullOrEmpty(labelText))
    {
        return MvcHtmlString.Empty;
    }
    (以下略)

解決

現行のModelMetadata.FromLambdaExpressionソースの290行目で、

containerType = memberExpression.Expression.Type;

とコンテナ型を求めている箇所がSystem.Web.Mvc.dllのバージョン2.0.0.0では

containerType = body.Member.DeclaringType;

となっているため、過去に不具合修正が入っているのだと思われます。

拡張メソッドでの回避方法

例えばLabelForの場合、以下のように拡張メソッドを実装すればModelMetadata.FromStringExpressionを利用します。なお拡張メソッドの利用にはweb.configの編集が必要です。

public static class HtmlHelperExtensions
{
    public MvcHtmlString LabelFor2<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
    {
        return html.Label(ExpressionHelper.GetExpressionText(expression));
    }
}

これでもダメであれば自分でModelMetadataHtmlHelperを作ってLabelForModelに渡すか、自分でHTMLを作るしかないように思われます。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *