问题 通用局部视图:如何将泛型类设置为模型?


我正在尝试在ASP.NET MVC应用程序中构建通用网格视图。

让我解释一些代码:

public interface ITrustGrid<T>
{
    IPagedList<T> Elements { get; set; }
    IList<IColumn<T>> Columns { get; set; }
    IList<string> Headers { get; }
}

这是一个类的接口,允许我在控制器中设置列和表达式。

我将实现传递给部分视图,如下所示:

<% Html.RenderPartial("SimpleTrustGridViewer", ViewData["employeeGrid"] as TrustGrid<EmployeeInfoDTO>); %>

问题是我无法弄清楚如何使局部视图呈现网格通用。

换句话说,我想转此:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<EmployeeInfoDTO>>" %>

变成这样的东西:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<T>>" %>

=>如何以最简单的方式使我的部分视图通用?

编辑:

我通过使用具有公共TrustGrid GetTrustGrid()方法的TrustGridBuilder解决了这个问题,该方法返回非泛型TrustGrid。 TrustGrid包含字符串而不是linq的东西。所以我在GetTrustGrid()方法中执行linq并将字符串放在TrustGrid对象中。

感谢大家帮助我走上正轨。


1531
2018-05-17 17:45


起源



答案:


这样做是不可能的。原因是 .aspx 将生成一个你没有太多控制权的类,你不能添加一个通用参数。我想最直接的方法是将其传递给 object


4
2018-05-17 17:48



嗯我试过了,但是无法弄清楚如何将它重新投入有用的东西。 - Thomas Stock
你几乎无法将它转换为任何有用的东西,但你仍然可以将它用作动态对象。 - Dovydas Navickas


答案:


这样做是不可能的。原因是 .aspx 将生成一个你没有太多控制权的类,你不能添加一个通用参数。我想最直接的方法是将其传递给 object


4
2018-05-17 17:48



嗯我试过了,但是无法弄清楚如何将它重新投入有用的东西。 - Thomas Stock
你几乎无法将它转换为任何有用的东西,但你仍然可以将它用作动态对象。 - Dovydas Navickas


您可以将要传递给此部分的所有模型类型从基类/接口继承,该基类/接口建立此部分视图将使用的基本行为并接受该类/接口类型的任何对象,或者只是拥有该视图接受任何类型的对象,然后使用反射来基于您的局部视图行为。

例:

public interface IDisplayModel
{
    string DisplayText{get;set;}
    string ImageUrl{get;set;}
    string AltText{get;set;}
}

public interface ITrustGrid<T> where T : IDisplayModel
{    
    IPagedList<T> Elements { get; set; }    
    IList<IColumn<T>> Columns { get; set; }    
    IList<string> Headers { get; }
}

<%@ Control Language="C#" 
    Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<IDisplayModel>>" %>

当然,你的 IDisplayModel 会根据您的期望行为而有所不同。这将允许您将任何内容传递给实现此基本接口的部分以建立一般行为。


6
2018-05-17 17:56



您能举一个关于如何实现反射解决方案的简短示例吗? - Thomas Stock
添加了示例并更改了contol继承属性以接受“ITrustGrid <IDisplayModel>”希望这是您正在寻找的。 - Adam Carr
谢谢,但我不喜欢这个解决方案,我必须使每个类实现IDisplayModel接口。我更喜欢一个解决方案,我可以在局部视图中添加一些东西以使其工作。也许我可以在我的ITrustGrid界面中添加一个Type成员?我现在正在尝试类似的东西,但我对这个通用的东西很新:) - Thomas Stock
您可以在视图中使用反射并让您的视图模型ITrustGrid <Object>然后使用反射来确定类型并在部分内部相应地显示逻辑,但这会使视图比我想要的更加臃肿。祝你好运。 - Adam Carr
唉,我无法弄清楚..一直在尝试各种各样的东西,但不能得到模型的类型。我尝试在我的界面中添加一个'Type'成员,但这也不起作用.. - Thomas Stock


我同意Mehrdad,据我所知,不可能制作通用视图。在我的一个项目中,我使用了一个非常类似的接口,然后将委托函数传递给处理每个项目的特定呈现的视图。

例如,我会使用带有附加字段的非泛型视图数据类:

public interface ITrustGrid {
    IPagedList Elements { get; set; }
    IList<IColumn> Columns { get; set; }
    IList<string> Headers { get; }

    Func<object, string> ElementRenderer { get; }
}

在主视图中,您将准备视图数据:

<%
ITrustGrid data = (ITrustGrid)ViewData["employeeGrid"];
data.ElementRenderer = new Func<object, string>(delegate(o) {
    var employee = (Employee)o;
    //render employee
    return html;
});

Html.RenderPartial("SimpleTrustGridViewer", data);
%>

在您的网格部分中,您将像往常一样处理网格,然后调用委托渲染每个单元格:

<%
foreach(var element in ViewData.Elements){
    %>
    <tr>
        <td><%=ViewData.ElementRenderer(element) %></td>
    </tr>
    <%
}
%>

当然,上面的代码只为每个元素呈现一个单元格,您必须创建一个稍微复杂的委托来呈现多个列(或传入一个委托数组,每列一个)。

我认为这将是最干净的方法之一,虽然有点麻烦。


1
2018-05-17 19:13



你带我走上正轨,谢谢。 - Thomas Stock


@Lck:

我在我的控制器中做了类似的事情:

var columns = new List<IColumn<EmployeeInfoDTO>>
                  {
                      new Column<EmployeeInfoDTO>("Full name", e => string.Format("{0} {1}", e.FirstName, e.Name)),
                      new Column<EmployeeInfoDTO>("Examination date", e => e.ExaminationDate.HasValue? string.Format("{0} days ago", currentDate.Subtract(e.ExaminationDate.Value).Days) : "Unknown")
                  };

var employeeGrid = new TrustGrid<EmployeeInfoDTO> { Columns = columns, Elements = GetEmployees(currentPageIndex)};

ViewData["employeeGrid"] = employeeGrid;

所以在我的部分视图中,我可以这样做:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<EmployeeInfoDTO>>" %>
<table>
    <thead>
        <tr>
            <%
                foreach (string header in Model.Headers)
                    Response.Write(Html.Th(header));
            %>
        </tr>
    </thead>
    <tbody>
        <%
            foreach (var element in Model.Elements)
            {
                Response.Write("<tr>");
                foreach (var column in Model.Columns)
                    Response.Write(Html.Td(column.ValueExpression(element)));
                Response.Write("</tr>");
            }
        %>
    </tbody>
</table>
<div class="pager">
    <%= Html.Pager(Model.Elements.PageSize, Model.Elements.PageNumber, Model.Elements.TotalItemCount)%>
</div>

如您所见,我的部分代码都不依赖于使用的类型。所以我仍然认为这是一个简单的解决方案。


0
2018-05-17 19:25



既然你的部分不依赖于使用的类型,你可以只使用Object吗? ITrustGrid <对象>? - Adam Carr
我得到“传入字典的模型项是'Helpers.TrustGrid类型1[LinqToSqExample.Model.EmployeeInfoDTO]' but this dictionary requires a model item of type 'Helpers.ITrustGrid1 [System.Object]'。“使用TrustGrid <EmployeeInfoDTO>调用局部视图时 - Thomas Stock
当我用TrustGrid <object>调用partial时,Model为null - Thomas Stock
如果我从TrustGrid(具体类)上的ITrustGrid <customtype>继承,我会得到与上面描述的相同的结果。如果我将TrustGrid继承更改为ITrustGrid <Object>,那么它对我有用。 - Adam Carr
嗯明天会试试。感谢检查出来。 - Thomas Stock