我有一个让我头疼的场景,我认为我的方向是正确的,现在我发现了另一个问题。这很复杂,让我们从数据库开始:
我有一个表,其中包含一个键、一个值、一个 typeId 和一些其他属性。我所关心的只是键和值。我的 View 模型包含我从 nhibernate 在存储库层中使用的数据对象映射的域对象的列表。在这里:
public class DomainModel
{
public string Key { get; set; }
public string Value { get; set; }
public TypeEnum Type { get; set; }
}
**注意,TypeEnum 与 Key 的 Value 的强类型无关。这只是对键/值进行分类的一种不同方式,以便我可以按该类型从数据库中提取它们。
简单。这是我的 View 模型:
public class ViewModel
{
public List<DomainModel> Models { get; set; }
}
我遇到的问题是键的值是不同的数据类型。有时它们是 bool 值,我想要一个 Html.CheckBoxFor(model => model.Value),有时它们是字符串,一个 Html.TextBoxFor(model => model.Value) 就足够了。
这是我的 Razor View :
@foreach (var setting in Model.Models)
{
<tr>
<td>@Html.Label(setting.Key)</td>
<td>@Html.TextBox(setting.Value)</td>
</tr>
}
在此处切分应用程序的最佳区域是什么,并进行一些类型检查或其他操作,以便我在页面上具有适当的 Html 元素?我什至会怎么做呢?我在这里错过了一些非常明显和简单的东西吗?此外,如何根据键的值获取键的显示名称属性?它们目前只是 PacalCasedBunchedDescriptionNames。我是不是离设计太远了还是怎么的?
最佳答案
我最终所做的只是让 View 和 View 模型保持平坦,而不是尝试在那里进行一些动态类型暗示。这也让我能够保持我的验证和其他属性整洁。
域模型仍然是一样的,我实际上最终删除了类型,并在我的业务层中创建了一个 SaveDomainModel,因为每次我保存/更新一个集合时,它只针对一种类型的设置:
public class SaveDomainModel
{
public List<DomainModel> DomainModels { get; set; }
public SettingTypeEnum SettingType { get; set; }
}
我将我的 DomainModel 更改为:
public class DomainModel
{
[Required]
public string Key { get; set; }
[Required]
public string Value { get; set; }
}
并展平我的 ViewModel:
public class EditViewModel
{
[DisplayName("Display Name:")]
[Required]
public int AnIntProp { get; set; }
[DisplayName("Another Display Name:")]
[Required]
public string HereIsAString { get; set; }
[DisplayName("Bool Display:")]
[Required]
public bool ImABool{ get; set; }
}
所以现在我的 Controller 对于 POST 看起来像这样:
[HttpPost]
public virtual ActionResult Edit(EditViewModel viewModel)
{
if (ModelState.IsValid)
{
SaveSettings(viewModel);
return RedirectToAction(MVC.Settings.Edit());
}
return View(viewModel);
}
private void SaveSettings(EditViewModel viewModel)
{
var settings = MapEditViewModelToDomainModels(viewModel);
var saveDomainModel = new SaveDomainModel
{
DomainModels = settings,
SettingType = SettingTypeEnum.Application
};
_settingsService.SaveSettings(saveDomainModel);
}
这确实是我之前没有发现的缺失链接,我在这篇文章中偶然发现了它:Enumerating through an object's properties (string) in C#
然后为了从平面 View 模型映射到域对象,我在 SaveSettings() 中使用了 Map... 函数。
private static List<DomainModel> MapEditViewModelToDomainModels(EditViewModel viewModel)
{
var settings = new List<DomainModel>();
var stringPropertyNamesAndValues = viewModel.GetType().GetProperties().Where(p => p.CanRead).Select(p => new {Name = p.Name, Value = p.GetValue(viewModel, null)});
foreach (var pair in stringPropertyNamesAndValues)
{
var model= new DomainModel
{
Key = pair.Name,
Value = pair.Value.ToString()
};
settings.Add(model);
}
return settings;
}
然后我就可以像这样保持我的视野平坦:
<tr>
<td>@Html.LabelFor(model => model.SomeString)</td>
<td>@Html.TextBoxFor(model => model.SomeString)</td>
</tr>
<tr>
<td>@Html.LabelFor(model => model.SomeBoolean)</td>
<td>@Html.CheckBoxFor(model => model.SomeBoolean)</td>
</tr>
...>
然后为了完成它,我在我的存储库中添加了一个 UpdateCollection(),显然,在从 DomainObj -> DataObj 映射之后,它在服务层中被调用。
public void SaveSettings(SaveDomainModel model)
{
var settings = MapDomainModelToList(model).AsQueryable();
_repository.UpdateCollection(settings);
}
private IEnumerable<DataObj> MapDomainModelToList(SaveDomainModel saveDomainModel)
{
var settings = new List<Setting>();
foreach (var domainModel in saveDomainModel.DomainModels)
{
var setting = GetSetting(domainModel.Key, saveDomainModel.SettingType);
if (!String.Equals(setting.Value, domainModel.Value, StringComparison.CurrentCultureIgnoreCase))
{
setting.Value = domainModel.Value;
setting.LastUpdated = DateTime.Now;
settings.Add(setting);
}
}
return settings;
}
public bool UpdateCollection(IQueryable<T> entities)
{
using (var transaction = _session.BeginTransaction())
{
foreach (var entity in entities)
{
_session.Update(entity);
}
transaction.Commit();
}
return true;
}
关于c# - 设计指南 - 来自数据库中松散类型键/值的动态 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20863855/