Страницы

Поиск по вопросам

четверг, 19 декабря 2019 г.

Столкнулся с проблемой, когда модель содержит много свойств. Как писать эффективнее?

#c_sharp #aspnet #list


Дело в том, что впервые столкнулся с моделью, где более 40 свойств, которые нужно
отобразить. 

[Display(Name = "Название 1")]
public string Name1 { get; set; }
[Display(Name = "Название 2")]
public int Name2 { get; set; }
[Display(Name = "Название 3")]
public int Name3 { get; set; }    
[Display(Name = "Название 4")]
public int Name4 { get; set; }


В представлении мне нужно отобразить данные в виде таблицы

@Html.LabelFor(c => common.Name1) @Html.LabelFor(c => common.Name2) @Html.LabelFor(c => common.Name3)
Мне кажется нелогичным такой способ вывода информации и подозреваю, что можно сделать это более легким способом, который пока не смог найти... Подскажите, пожалуйста. Есть ли что-либо похожее на это @{ foreach(var DisplayName in Model.ObjectPropertyAttributies) }
DisplayName


Ответы

Ответ 1



Если по работе нужно создавать много однотипных файлов то можно в сторону кодогенерации посмотреть: https://msdn.microsoft.com/en-us/library/bb126445.aspx Я это использую, чтобы делать заготовки под view model: <#@ template debug="true" hostSpecific="false" #> <#@ output extension=".cs" #> <#@ Assembly Name="System.Core" #> <#@ Assembly Name="$(SolutionDir)packages\Prism.Core.6.2.0\lib\net45\Prism.dll" #> <#@ import namespace="System" #> <#@ import namespace="Prism.Mvvm" #> <# // This template generates the code for a ViewModel which is based on some model. // Also supposed that used MVVM pattern and Prism Library // Full name of type for which will be generated ViewModel Type modelType = typeof(someType); PopulateTypeNameProperties(modelType); // Namecpase inside which the code of ViemModel will be put string space = "Solution.ViewModel"; // Begining template's code #>namespace <#= space#> { // usings using <#= modelType.Namespace#>; public class <#= modelType.Name #>ViewModel : BindableBase { #region Fields <# foreach(var tuple in this.typeNameProperties) { #> private <#= tuple.Item1 #> <#= tuple.Item3 #>; <#}#> #endregion #region Property for binding <# foreach(var tuple in this.typeNameProperties) { #> public <#= tuple.Item1 #> <#= tuple.Item2 #> { get { return this.<#= tuple.Item3 #>; } set { this.SetProperty(ref this.<#= tuple.Item3 #>, value); } } <#}#> #endregion public <#= modelType.Name #>ViewModel() {} public <#= modelType.Name #>ViewModel(<#= modelType.Name #> model) { <# foreach(var tuple in this.typeNameProperties) { #> this.<#= tuple.Item3 #> = model.<#= tuple.Item2 #>; <#}#>} public void UpdateViewModel(<#= modelType.Name #> model) { <# foreach(var tuple in this.typeNameProperties) { #> this.<#= tuple.Item2 #> = model.<#= tuple.Item2 #>; <#}#>} public static explicit operator <#= modelType.Name #> (<#= modelType.Name #>ViewModel viewModel) { <# int beforeLast = typeNameProperties.Count - 1; #> return new <#= modelType.Name #> { <# for (int i = 0; i < beforeLast; ++i) { #> <#= typeNameProperties[i].Item2 #> = viewModel.<#= typeNameProperties[i].Item3 #>, <#}#><# if (typeNameProperties.Count > 1) { this.Write(string.Format("\t\t{0} = viewModel.{1}", typeNameProperties[beforeLast].Item2, typeNameProperties[beforeLast].Item3)); } this.Write("\r\n"); #> }; } } } <# // Ending template's code #> <#+ // The storage for couple type - name of Properties private List> typeNameProperties = new List>(); private void PopulateTypeNameProperties(Type modelType) { foreach (var p in modelType.GetProperties()) { if (string.CompareOrdinal(p.Name, "ErrorMessage") == 0) { continue; } string type = GetShortTypeName(p.PropertyType.Name); var name = p.Name; char firstLetter = name[0]; var property = char.ToUpperInvariant(firstLetter).ToString() + name.Substring(1); var field = char.ToLowerInvariant(firstLetter).ToString() + name.Substring(1); typeNameProperties.Add(new Tuple(type, property, field)); } } #> <#+ private string GetShortTypeName(string typeName) { switch(typeName) { case "Int32": return "int"; / ....... case "Object": return "object"; default: return typeName; } } #> Правда есть немного заморочек с отступами, и выглядит не очень читабельно. Но не нужно тратить время на медитативный набор свойств view model. Результат вот такой: namespace Solution.ViewModel { // usings public class CurrencyInfoViewModel : BindableBase { #region Fields private string name; private string shortName; private DateTime modifyTime; private int id; #endregion #region Property for binding public string Name { get { return this.name; } set { this.SetProperty(ref this.name, value); } } public string ShortName { get { return this.shortName; } set { this.SetProperty(ref this.shortName, value); } } public DateTime ModifyTime { get { return this.modifyTime; } set { this.SetProperty(ref this.modifyTime, value); } } public int Id { get { return this.id; } set { this.SetProperty(ref this.id, value); } } #endregion public CurrencyInfoViewModel() {} public CurrencyInfoViewModel(CurrencyInfo model) { this.name = model.Name; this.shortName = model.ShortName; this.modifyTime = model.ModifyTime; this.id = model.Id; } public void UpdateViewModel(CurrencyInfo model) { this.Name = model.Name; this.ShortName = model.ShortName; this.ModifyTime = model.ModifyTime; this.Id = model.Id; } public static explicit operator CurrencyInfo (CurrencyInfoViewModel viewModel) { return new CurrencyInfo { Name = viewModel.name, ShortName = viewModel.shortName, ModifyTime = viewModel.modifyTime, Id = viewModel.id }; } } }

Ответ 2



Ответ на вопрос после значительной правки. Вероятно, можно написать код, который будет действовать через рефлексию и вытягивать информацию о полях. (Не пробовал, поэтому не знаю, есть ли какие-то подводные камни - но путь выглядит вполне рабочим). Рефлексия - штука небыстрая, поэтому вполне возможно, что овчинка не стоит выделки, я лично не слышал, чтобы кто-то шёл таким путём. Классический способ - это использование кодогенерации. Правый клик на папке контроллеров в solution explorer, Add - Controller, шаблон "MVC Controller with views, using Entity Framework" в котором укажите вашу модель, а студия вам сгенерирует полностью контроллер, содержащий все CRUD методы, а также все необходимые view (и для создания-редактирования, и для списка и для детального просмотра). Ответ на предыдущую версию ответа: Если вам дали утверждённый макет, в котором таблица на сорок строк - то как бы нелогичным вам это не казалось - нужно взять и заверстать. Либо вам нужно пойти к проектировщику интерфейса и объяснить ему, что сорок колонок на экране мешают работать, а не помогают. Ну или не дизайнера переубеждать, а руководство. Это правильное решение проблемы. Нужно начать прорабатывать пользовательские сценарии, смотреть в каких какие колонки нужны. Возможно, что окажется, что и не нужно было столько полей. Это - типичные проблемы энтерпрайза: В моей практике был случай, когда удалось убедить руководство, что не нужно заставлять вводить 96 полей, достаточно всего пяти. Если же вам аналитики и руководство скажет, что тут только так нужно, чтобы на экране был интерфейс чуть ли не гугл таблиц на экране с бесконечным количеством столбцов, то есть ещё вариант, только они ни разу не простой. Сделайте интерфейс, который позволяет скрывать/показывать столбцы.

Ответ 3



Если вам нужен просто цикл по всем свойствам - такой способ есть. Для начала нужно получить метаданные вашей модели. Если это основная модель страницы, то все просто - это ViewData.ModelMetadata. Если же, судя по таблице, это элемент какой-нибудь коллекции, то получить его метаданные проще через вызов new ViewDataDictionary<ТипЭлемента>(элемент).ModelMetadata. Точнее, лучше сначала создать ViewDataDictionary вот так: var data = new ViewDataDictionary<ТипЭлемента>(), а потом менять ему модель - это немного ускорит работу. Ну а дальше все просто: у ModelMetadata есть все требуемые свойства: @{ var data = new ViewDataDictionary<ТипЭлемента>(null); @foreach (var propertyMetadata in data.ModelMetadata.Properties) { } @foreach (var item in какаяТоКоллекция) { data.Model = item; @foreach (var propertyMetadata in data.ModelMetadata.Properties) { } } }
@propertyMetadata.DisplayName;
@propertyMetadata.Model;


Комментариев нет:

Отправить комментарий