如何在Asp.Net MVC Razor中创建“通用”控件?

时间:2013-07-03 05:33:34

标签: c# asp.net-mvc-3 generics razor

我正在尝试编写一个通用(在许多地方有用)控件,我可以在整个公司中重复使用。

我在视图和viewmodel中遇到了实际的C#泛型问题。

以下是我正在尝试做的一个例子:

通用部分视图:(_Control.cshtml

@model SimpleExample<dynamic> 

@Model.HtmlTemplate(Model.Data)

ViewData:(SimpleExample.cs

public class SimpleExample<T>
{
    public T Data;
    public Func<T, System.Web.WebPages.HelperResult> HtmlTemplate;
}

用法示例:(FullView.cshtml

@model Foo.MyViewData

@Html.Partial("_Control", new SimpleExample<MyViewData>
{
    Data = Model,
    HtmlTemplate = @<div>@item.SomeProperty</div>
})

我正在寻找的功能的重要部分是消费者在编写Html内联时获取一个类型化对象,以便他们可以使用Intellisense(如FullView.cshtml中所示)。

所有内容编译良好且智能感知正在运行,但我将错误视为运行时:

The model item passed into the dictionary is of type 
'AnytimeHealth.Web.ViewData.SimpleExample`1[Foo.MyViewData]', 
but this dictionary requires a model item of type 
'AnytimeHealth.Web.ViewData.SimpleExample`1[System.Object]'.

我已经读过,我可以在我的通用类型上使用covarience来实现这一点,但我不知道该怎么做。

你能告诉我如何才能让它发挥作用吗?

2 个答案:

答案 0 :(得分:3)

更改_Control.cshtml中的定义:

更改此@model SimpleExample<dynamic>

@model dynamic它会起作用,但会丢失SimpleExample

的智能感知

MyViewData的intellisense仍然有效。

我认为这是因为动态类型将在运行时知道,但是泛型的类型

需要早期(可能是编译时间),此时只有object是已知的。

答案 1 :(得分:3)

您可以使用通用对象,然后使用反射渲染此对象的属性(使用帮助程序列出属性)。这与Twitter Bootstrap用于MVC 4的方法相同(为方便起见,此代码的部分内容已被复制):http://nuget.org/packages/twitter.bootstrap.mvc4

_Control.cshtml

@model Object
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>@Model.GetLabel()</legend>
        @foreach (var property in Model.VisibleProperties())
        {
            using(Html.ControlGroupFor(property.Name)){
                @Html.Label(property.Name)
                @Html.Editor(property.Name)
                @Html.ValidationMessage(property.Name, null)
            }
        }
        <button type="submit">Submit</button>
    </fieldset>
}

Helper.cs

public static string GetLabel(this PropertyInfo propertyInfo)
{
    var meta = ModelMetadataProviders.Current.GetMetadataForProperty(null, propertyInfo.DeclaringType, propertyInfo.Name);
    return meta.GetDisplayName();
}

public static PropertyInfo[] VisibleProperties(this IEnumerable Model)
{
    var elementType = Model.GetType().GetElementType();
    if (elementType == null)
    {
        elementType = Model.GetType().GetGenericArguments()[0];
    }
    return elementType.GetProperties().Where(info => info.Name != elementType.IdentifierPropertyName()).ToArray();
}

public static PropertyInfo[] VisibleProperties(this Object model)
{
    return model.GetType().GetProperties().Where(info => info.Name != model.IdentifierPropertyName()).ToArray();
}