C# 表达式<;Func<;t模型,字符串>&燃气轮机;当TModel是接口时,类型永远不是传递的实现

C# 表达式<;Func<;t模型,字符串>&燃气轮机;当TModel是接口时,类型永远不是传递的实现,c#,generics,asp.net-mvc-5,html-helper,C#,Generics,Asp.net Mvc 5,Html Helper,我创建了一个自定义的HtmlHelper来在网页上呈现地址。地址需要根据地址所在的国家/地区进行动态设置。为此,我有一个带有多个实现的简单接口。我的控制器将特定的实现传递给视图,该视图被强类型化到接口。调试时,视图显示正确的实现类型,我将该模型作为泛型类型参数传递给我的自定义HtmlHelper。此自定义HtmlHelper仅接受类型为IAddress或其实现的模型,但无论传递到TModel的类型是什么,TModel始终是IAddress。这是一个问题,因为我在我自己的助手中使用现有的htmlH

我创建了一个自定义的HtmlHelper来在网页上呈现地址。地址需要根据地址所在的国家/地区进行动态设置。为此,我有一个带有多个实现的简单接口。我的控制器将特定的实现传递给视图,该视图被强类型化到接口。调试时,视图显示正确的实现类型,我将该模型作为泛型类型参数传递给我的自定义HtmlHelper。此自定义HtmlHelper仅接受类型为IAddress或其实现的模型,但无论传递到TModel的类型是什么,TModel始终是IAddress。这是一个问题,因为我在我自己的助手中使用现有的htmlHelper(如Html.EditorFor和Html.LabelFor)来呈现地址的字段和标签,包括提交时的正确验证,所以各个实现上的属性

我认为问题在于视图是强类型的,而HtmlHelper忽略了实际的模型类型,直接使用视图类型,我如何才能避免这种情况


接口和实现类:

public interface IAddress
{
    // Dumbed down to one property to save space
    String City { get; set; }
    String State { get; set; }
}

public class AddressUS : IAddress
{
    // Required and displayed on page.
    [DisplayName("City")]
    [Required(ErrorMessage = "City is required!")]
    public String City { get; set; }

    // Required and displayed on page.
    [DisplayName("State")]
    [Required(ErrorMessage = "State is required!")]
    public String State { get; set; }
}

public class AddressJP : IAddress
{
    // Required and displayed on page.
    [DisplayName("Prefecture")]
    [Required(ErrorMessage = "Prefecture is required!")]
    public String City { get; set; }

    // Not required and not displayed on page.
    [DisplayName("State")]
    public String State { get; set; }
}

控制器操作:

public ActionResult DisplayAddress()
{
    // Returning a specific type for testing.
    AddressJP address = new AddressJP() { City = "Test" };
}

视图:

@*
设置为接口以接受所有实现,但也可能是问题的原因。
设置为AddressJP,但我需要它是动态的。
*@
@模特裙
@使用Custom.htmlex
@Html.AddressEditorForModel(true)

HtmlHelper:

public static MvcHtmlString AddressEditorForModel<TModel>(this HtmlHelper<TModel> helper, Boolean showLabels) where TModel : IAddress
    {
        // TModel is always IAddress unless I change the view to be a specific implementation.

        StringBuilder sb = new StringBuilder();

        PropertyInfo pInfo = typeof(TModel).GetProperty("City");
        ParameterExpression paramExpr = Expression.Parameter(typeof(TModel));
        MemberExpression propertyAccess = Expression.MakeMemberAccess(paramExpr, pInfo);
        var lambdaExpr = Expression.Lambda<Func<TModel, string>>(propertyAccess, paramExpr);

        if (showLabels)
            sb.AppendLine(helper.LabelFor(lambdaExpr).ToString());
        sb.AppendLine(helper.EditorFor(lambdaExpr).ToString());

        return MvcHtmlString.Create(sb.ToString());
    }
public static MvcHtmlString AddressEditorForModel(此HtmlHelper助手,布尔showLabels),其中TModel:iadAddress
{
//除非我将视图更改为一个特定的实现,否则TModel始终为空。
StringBuilder sb=新的StringBuilder();
PropertyInfo pInfo=typeof(TModel).GetProperty(“城市”);
ParameterExpression parameterxpr=Expression.Parameter(typeof(TModel));
MemberExpression propertyAccess=Expression.MakeMemberAccess(参数xpr,pInfo);
var lambdaExpr=Expression.Lambda(propertyAccess,paramExpr);
如果(显示标签)
sb.AppendLine(helper.LabelFor(lambdaExpr.ToString());
sb.AppendLine(helper.EditorFor(lambdaExpr.ToString());
返回MvcHtmlString.Create(sb.ToString());
}

您不必使用反射来获取值。您已经指定了泛型类型约束,因此.NET知道
TModel
必须是
IAddress

你应该能够做到这一点

var city = helper.ViewData.Model.City;
事实上,您应该能够完全取消泛型方法-只需使用
HtmlHelper
作为参数,它就可以正常工作


此外,您可以使用
helper.Label
等,而不是
helper.LabelFor
——它们的工作方式相同,但不需要表达式,只需要属性的名称。

使用helper.ViewData.Model.City可以很好地获取该属性的值,但我需要的是使用这些属性上的属性。例如标签的显示名和编辑器上的验证。我不知道这怎么会允许这样做?@WebDevNewbie在使用
helper.Label
时,它不会自动工作吗?在任何情况下,请查看
ModelMetadataProviders.Current.GetMetadataForType
-它会自动根据类型提供所有这些属性,无需自己处理属性。helper.Label(helper.ViewData.Model.City)将标签值设置为City属性的值,不是城市属性上属性的值。LabelFor,但这需要一个需要TModel的表达式。我将在这里尝试一下ModelMetadataProviders,看看它的发展方向:)@WebDevNewbie真的吗?它不应该这样做。。。那么
helper.DisplayName
呢?@luan-是的,DisplayName也做同样的事情。为了确保我的助手没有把它弄糟,我直接将它们添加到视图中,结果是一样的。
var city = helper.ViewData.Model.City;