我有一个令我头痛的场景,我认为我正朝着正确的方向前进,现在我发现了另一个问题。这很复杂,让我们从数据库开始:
我有一个带有键的表,一个值,一个typeId和一些其他属性。我所关心的只是关键和价值。我的viewmodel包含我从nhibernate在存储库层中使用的数据对象映射的域对象的列表。这是:
public class DomainModel
{
public string Key { get; set; }
public string Value { get; set; }
public TypeEnum Type { get; set; }
}
**注意,TypeEnum与Key的值的强类型无关。这只是对键/值进行分类的一种不同方式,以便我可以按类型从数据库中提取它们。
简单。这是我的viewmodel:
public class ViewModel
{
public List<DomainModel> Models { get; set; }
}
我遇到的问题是密钥的值是不同的数据类型。有时它们是布尔值,我想要一个Html.CheckBoxFor(model =&gt; model.Value),有时它们是字符串,而Html.TextBoxFor(model =&gt; model.Value)就足够了。
这是我的剃刀观点:
@foreach (var setting in Model.Models)
{
<tr>
<td>@Html.Label(setting.Key)</td>
<td>@Html.TextBox(setting.Value)</td>
</tr>
}
应用程序在此处切片的最佳区域是什么,并进行某些类型检查或某些事情,以便我在页面上有适当的Html元素?我怎么会这样做呢?我错过了一些非常明显和简单的东西吗?此外,如何根据Key的值获取Keys的显示名称属性?他们目前只是PacalCasedBunchedDescriptionNames。我在这里设计还是什么呢?
答案 0 :(得分:1)
我最终做的只是让视图和视图模型保持平坦,而不是试图在那里做一些动态类型的暗示。这也使我能够保持我的验证和其他属性的整洁。
Domain Model仍然是相同的,我实际上最终删除了Type,并且在我的Business Layer中创建了一个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; }
}
所以现在我的控制器看起来像是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#
然后从平面视图模型映射到Domain obj我在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>
...>
然后为了完成它,我在我的Repository中添加了一个UpdateCollection(),在从DomainObj映射后,在服务层中调用了它 - &gt; 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;
}
答案 1 :(得分:0)
一种方法是使用MVC功能来实现它,而不是触及nhibernate细节。我的意思是,nhibernate只是你的数据层吗?这根本不会影响您的表示层。
您的模型上已经有TypeEnum
属性。
我想这将定义属性是否应显示为复选框,文本框或其他...如果是,请为您的DomainModel
类型编写自定义 editortemplate ,并将逻辑放在一个位置如何呈现DomainModel
的实例。
如果您对MVC中的编辑器模板感到好奇,请查看Scott's blog或this one
为您举例说明这种情况:
模特:
public class Entity : List<Property>
{
public Entity()
{
}
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
public DisplayType DisplayType { get; set; }
}
public enum DisplayType
{
TextBox,
Checkbox
}
用于测试的控制器/动作:
public ActionResult Index()
{
var entity = new Entity();
entity.Add(new Property()
{
DisplayType = DisplayType.Checkbox,
Name = "Check1",
Value = "True"
});
entity.Add(new Property()
{
DisplayType = DisplayType.Checkbox,
Name = "Check2",
Value = "False"
});
entity.Add(new Property()
{
DisplayType = DisplayType.TextBox,
Name = "Input1",
Value = ""
});
//ViewBag.Entity = entity;
return View(entity);
}
View可能如下所示:
@using WebApplication6.Models
@model WebApplication6.Models.Entity
@{
ViewBag.Title = "Edit Entity";
}
<h2>Edit Entity</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Entity</h4>
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@for (var i = 0; i < Model.Count;i++ )
{
<div class="form-group">
@Html.EditorFor(m => m[i], "PropertyEditor")
</div>
}
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
所有魔法现在隐藏在@Html.EditorFor(m => m[i], "PropertyEditor")
内
在“查看/共享”下创建文件夹EditorTemplates
,然后为您的模板添加文件,例如PropertyEditor.cshtml
模板可能如下所示:
@model WebApplication6.Models.Property
@if (Model != null)
{
<label for="@Model.Name" class="col-sm-2 control-label">@Model.Name</label>
switch (Model.DisplayType)
{
case WebApplication6.Models.DisplayType.TextBox:
<div class="col-sm-10">@Html.TextBox(Model.Name, Model.Value)</div>
break;
case WebApplication6.Models.DisplayType.Checkbox:
<div class="col-sm-10">@Html.CheckBox(Model.Name, bool.Parse(Model.Value))</div>
break;
}
}