ASP.net MVC - рендеринг списка, содержащего разные типы, с разным представлением для каждого типа
-
12-09-2019 - |
Вопрос
Представьте, что у меня есть список объектов, которые реализуют интерфейс с именем ISummary Объекты в этом списке МОГУТ обладать дополнительными свойствами, т.е.
public interface ISummary {
Guid Id {get;set;}
string Title {get;set;}
DateTime Created {get;set;}
}
public class GigSummary: ISummary {
Guid Id {get;set;}
string Title {get;set;}
DateTime Created {get;set;}
string VenueName {get;set}
string Band {get;set;}
}
public class NewsSummary: ISummary {
Guid Id {get;set;}
string Title {get;set;}
DateTime Created {get;set;}
string Author{get;set}
}
Теперь я передаю этот список объектов Gigs и News Summary (в виде списка ISummary) в представление в качестве модели.
Я хочу отобразить этот список, используя разные части для каждого типа, содержащегося в списке.
Как я могу это сделать ASP.NET MVC?
Решение
Самый очевидный способ, который я могу придумать, - это сделать что-то вроде:
foreach(ISummary summ in listOfISummary) {
Html.RenderPartial(String.Fomat("~/Views/Shared/{0}Renderer.ascx", summ.GetType.ToString()), summ, ViewData);%>
}
и создайте строго типизированное представление с соглашением об именовании, например NewsSummaryRenderer.ascx .
Я ожидаю, что вы могли бы перенести это во вспомогательный метод, но я бы добавил это к одному из существующих помощников с помощью метода расширения, а не помещал это в исходный код, как предлагалось ранее.
Другие советы
Вы могли бы поместить вспомогательный метод в кодовую строку представления, а затем сделать что-то вроде:
Type modelType = this.Model.GetType();
if (modelType == typeof(NewsSummary)) this.RenderPartial("newspartial", this.Model as NewsSummary);
else if (modelType == typeof(GigSummary)) this.RenderPartial("gigpartial", this.Model as GigSummary);
Льюис на правильном пути.Я бы пошел немного другим путем - чтобы оба "виджета" были расширены из общего базового класса, который предоставлял информацию об используемых именах представлений.Затем добавьте метод расширения в свой класс page для "рендеринга виджета", который мог бы создать соответствующий вид на месте.
Ознакомьтесь с образцом приложения Kona ASP.NET MVC для получения рабочего примера этой концепции.
Я бы создал расширение HtmlHelper, которое делало бы это.Вот некоторый псевдокод, который потрясающе похож на c # и действительно может работать:
public static void TemplatedList<T>(this HtmlHelper me, IEnumerable<T> items,
IDictionary<Type, Action<T>> templates)
{
foreach(var item in items)
{
var template = templates[item.GetType()];
if(template != null) template(item);
}
}
Я бы использовал это так:
<% HtmlHelper.TemplatedList(ViewData.Model, new Dictionary
{
{typeof(GigSummary), x => %>
<div class="gigSummary">
SUP! GIG ANNOUNCEMENT FOR <%= x.Band %>!!
What: <%= x.Title %>
When: <%= x.Created %>
Who: <%= x.Author %>
</div>
<%}
// add more type/template pairs here
}); %>
Вот простой метод расширения, который вы можете создать для извлечения только тех типов, которые вам нужны:
public static class Extensions
{
public static IEnumerable<U> ExtractOfType<U, T>(this IEnumerable<T> list)
where T : class
where U : class
{
foreach (var item in list)
{
if (typeof(U).IsAssignableFrom(item.GetType()))
{
yield return item as U;
}
}
}
}
Тест:
public interface IBaseInterface
{
string Foo { get; }
}
public interface IChildInterface : IBaseInterface
{
string Foo2 { get; }
}
public interface IOtherChildIntreface : IBaseInterface
{
string OtherFoo { get; }
}
public class BaseImplementation : IBaseInterface
{
public string Foo { get { return "Foo"; } }
}
public class ChildImplementation : IChildInterface
{
public string Foo2 { get { return "Foo2"; } }
public string Foo { get { return "Foo"; } }
}
public class OtherChildImplementation : IOtherChildIntreface
{
public string OtherFoo { get { return "OtherFoo"; } }
public string Foo { get { return "Foo"; } }
}
....
List<IBaseInterface> b = new List<IBaseInterface>();
b.Add(new BaseImplementation());
b.Add(new ChildImplementation());
b.Add(new OtherChildImplementation());
b.Add(new OtherChildImplementation());
foreach (var s in b.ExtractOfType<IOtherChildIntreface, IBaseInterface>())
{
Console.WriteLine(s.GetType().Name);
}
При этом будут получены все элементы в списке, относящиеся к производному типу, который вы ищете.Итак, в вашем контроллере передайте весь список в представление.Затем создайте частичные представления, которые принимают IEnumerable того типа, который необходим partial, и в вашем основном представлении вызовите этот метод расширения и передайте результат этим отдельным частичным представлениям.