Каков наилучший способ отображения в представлении большего количества типов, полученных из абстрактной базы?

Например, у меня есть эти три класса:

public abstract class AbstractBase 
{
    public int A { set; get; }
}

public class Derived1 : AbstractBase
{
    public int B { set; get; }
    public int C { set; get; }
}

public class Derived2 : AbstractBase
{
    public int D { set; get; }
}

И сервис, который возвращается со списком AbstractBase

public class AbstractBaseService
{
    public IEnumerable<AbstractBase> GetInstances()
    { 
        var results = new List<AbstractBase>();

        // TODO: Get data

        return results;
    }
}

И я хочу отобразить эти классы в строго типизированном ASP.NET MVC View в таблице (или GridView в WF). Так какой же самый лучший способ? А что если я хочу добавить опцию редактирования?

введите описание изображения здесь

1 ответ

Вы можете использовать шаблоны отображения в этом сценарии.

Идея состоит в том, чтобы создать шаблон отображения для каждого конкретного производного типа и оставить MVC задачу выяснения конкретного типа каждого элемента в списке во время выполнения, а затем подобрать соответствующий шаблон.

Представьте, что у нас есть следующий метод действия контроллера, который возвращает представление для отображения списка объектов AbstractBase:

public ActionResult Foo()
{
    var list = new List<AbstractBase>()
    {
        new Derived1{ A = 1, B = 2, C = 3},
        new Derived2{ A = 1, D = 4},
    };
    return View(list);
}

Вы можете создать следующие шаблоны:

~ / Views / Shared / DisplayTemplates / Derived1.cshtml

@model MvcApplication1.Models.Derived1
<td>
    @Html.DisplayFor(m => m.A)
</td>
<td>
    @Html.DisplayFor(m => m.B)
</td>
<td>
    @Html.DisplayFor(m => m.C)
</td>
<td></td>

~ / Views / Shared / DisplayTemplates / Derived2.cshtml

@model MvcApplication1.Models.Derived2
<td>
    @Html.DisplayFor(m => m.A)
</td>
<td></td>
<td></td>
<td>
    @Html.DisplayFor(m => m.D)
</td>

К сожалению, поскольку вы хотите отобразить элементы в таблице, вам нужно будет предоставить пустые элементы tr для столбцов, которые не относятся к каждому конкретному типу.

И основной вид ~/Views/Home/Foo.cshtml будет выглядеть так:

@model IEnumerable<MvcApplication1.Models.AbstractBase>           
<table>
    <thead>
        <tr>
            <th>Col A</th>
            <th>Col B</th>
            <th>Col C</th>
            <th>Col D</th>
        </tr>
    </thead>    
    <tbody>
        @foreach(var item in Model)
        {
            <tr>
                @Html.DisplayFor(m => item)
            </tr>
        }
    </tbody>
</table>

Он берет список объектов AbstractBase и строит тело таблицы, перебирая список и вызывая DisplayFor для каждого элемента. MVC во время выполнения проверит конкретный тип каждого элемента, а затем выполнит поиск соответствующего представления в папке DisplayTemplates.

Это самый простой подход, когда поля в общем базовом классе отображаются каждым конкретным шаблоном производного класса. Однако имеет смысл иметь общий шаблон для полей в базовом классе.

Вы можете создать другой шаблон отображения для полей в базовом классе: ~/Views/Shared/DisplayTemplates/AbstractBase.cshtml

@model MvcApplication1.Models.AbstractBase
<td>
    @Html.DisplayFor(m => m.A)
</td>

Как будет называться этот шаблон для полей в общем базовом классе, зависит от того, используете вы MVC 4 или нет. Если вы используете MVC 4, вы можете вызывать базовый шаблон из каждого из шаблонов для производных классов. Например, шаблон для Derived1 будет выглядеть так:

@model MvcApplication1.Models.Derived1
@Html.DisplayFor(m => m, "AbstractBase")
<td>
    @Html.DisplayFor(m => m.B)
</td>
<td>
    @Html.DisplayFor(m => m.C)
</td>
<td></td>

Если вы не используете MVC 4, печально вложенные редакторы экрана не работают, поэтому строка @Html.DisplayFor(m => m, "AbstractBase") выше ничего не сделаю.

Ваш первый и лучший вариант за пределами MVC4 - вручную отрисовать шаблон для базового класса как частичное представление. Эта строка будет заменена @Html.Partial("~/Views/Shared/DisplayTemplates/AbstractBase.cshtml", Model)

Другой вариант, который я бы порекомендовал вам избегать, - это перенести вызов шаблона абстрактного класса в главное представление вместо вызова из каждого из шаблонов производных классов. Это может даже не быть допустимым вариантом, если шаблон для базового класса должен быть встроен в некоторый элемент, сгенерированный каждым из производных классов. Например, это не будет возможным, если столбцы базового класса были не все в начале (или конце) каждой строки

Итак, основной вид, который отображает вид, будет делать:

@foreach(var item in Model)
{
    <tr>
        @Html.DisplayFor(m => item, "AbstractBase")
        @Html.DisplayFor(m => item)
    </tr>
}

Я бы всегда придерживался вызова базового шаблона из каждого из производных шаблонов, и я бы сказал, что вы должны быть очень осторожны, если вы когда-нибудь обнаружите, что используете последний подход. В противном случае вам нужно помнить, что звонил @Html.DisplayFor(m => item, "AbstractBase") каждый раз, когда вы хотите отобразить производный шаблон и некоторые html-проекты, возможно, даже не удастся достичь с помощью этого подхода.

Надеюсь, поможет!

Другие вопросы по тегам