我有一个应用程序,用于在数据库中存储有关顾问的信息。该模型是实体框架模型,数据库表是顾问,与许多其他表(WorkExperiences,Programs,CompetenceAreas等)具有一对多的关系。现在,当我想在View中创建一个新的Consultant对象时,我真的只想将一个Consultant对象作为模型传递给View。但是对于其中一个,有人建议我(Collection of complex child objects in Asp.Net MVC 3 application?)我不应该这样做,而是使用ViewModels。其次,也许这就是原因,当我尝试发布Consultant对象(如果在View中使用它作为模型)时,我得到一个错误,说“EntityCollection已经初始化”,并且错误的原因似乎是WorkExperiences等对象的集合。
所以我的第一个问题是为什么我收到这个错误。
但更重要的是,如果我应该使用ViewModel,我该如何正确地做到这一点?因为我事实上已经尝试了一些东西,并让它发挥作用。但是......代码很糟糕。任何人都可以请告诉我我应该做些什么而不是让它更干净地工作?
让我告诉你我拥有的东西(它再次起作用,但代码是噩梦):
GET Create方法:
public ActionResult Create()
{
Consultant consultant = new Consultant();
ConsultantViewModel vm = GetViewModel(consultant);
return View(vm);
}
创建“ViewModel”的Helper方法(如果这实际上是ViewModel应该是这样的):
private ConsultantViewModel GetViewModel(Consultant consultant)
{
ConsultantViewModel vm = new ConsultantViewModel();
vm.FirstName = consultant.FirstName;
vm.LastName = consultant.LastName;
vm.UserName = consultant.UserName;
vm.Description = consultant.Description;
vm.Programs = consultant.Programs.ToList();
vm.Languages = consultant.Languages.ToList();
vm.Educations = consultant.Educations.ToList();
vm.CompetenceAreas = consultant.CompetenceAreas.ToList();
vm.WorkExperiences = consultant.WorkExperiences.ToList();
return vm;
}
POST Create方法:
[HttpPost]
[ValidateInput(false)] //To allow HTML in description box
public ActionResult Create(ConsultantViewModel vm, FormCollection collection)
{
try
{
Consultant consultant = CreateConsultant(vm);
_repository.AddConsultant(consultant);
_repository.Save();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
创建Consultant对象的Helper方法(这个方法特别糟糕,我必须检查集合是否为null,以防用户决定不在这些列表中添加任何内容......):
private Consultant CreateConsultant(ConsultantViewModel vm)
{
Consultant consultant = new Consultant();
consultant.Description = vm.Description;
consultant.FirstName = vm.FirstName;
consultant.LastName = vm.LastName;
consultant.UserName = vm.UserName;
if (vm.Programs != null)
foreach (var program in vm.Programs)
consultant.Programs.Add(program);
if (vm.Languages != null)
foreach (var language in vm.Languages)
consultant.Languages.Add(language);
if (vm.Educations != null)
foreach (var education in vm.Educations)
consultant.Educations.Add(education);
if (vm.WorkExperiences != null)
foreach (var workExperience in vm.WorkExperiences)
consultant.WorkExperiences.Add(workExperience);
if (vm.CompetenceAreas != null)
foreach (var competenceArea in vm.CompetenceAreas)
consultant.CompetenceAreas.Add(competenceArea);
return consultant;
}
所以,它再次起作用,但是它远没有像我可以直接使用Consultant对象那样干净(如果不是因为“EntityCollection已经初始化”错误“......)。所以我应该怎么做呢? ?
答案 0 :(得分:4)
首先,你不应该使用你的实体对象作为viewmodel,因为(我现在至少可以想到两个原因,但还有更多):
您不希望公开敏感数据,例如“Id”或“密码”。想象一下,您的顾问有Id
,恶意用户打开编辑顾问页面并回发不同的Id
。因此,恶意用户将成功更新不同的Consultant
。
目前,您在视图中显示的内容与Consultant
对象的外观相对应。但是如果你想添加不属于Consultant
对象的额外信息(就像复选框字段一样简单)。在这种情况下,您必须重写相当多的代码,创建ViewModel,映射它等。如果您从一开始就遵循ViewModel模式,您可以随时进行这种简单的更改。
关于您的代码 - 您可以尝试将AutoMapper
与Nested Mappings一起用于此类转换。即使你不这样做,也可以通过使用投影来使代码更清晰。
private ConsultantViewModel GetViewModel(Consultant consultant)
{
return new ConsultantViewModel
{
FirstName = consultant.FirstName,
LastName = consultant.LastName,
...
vm.Programs = consultant.Programs.ToList(),
...
};
}
private Consultant CreateConsultant(ConsultantViewModel vm)
{
var consultant = new Consultant
{
Description = vm.Description,
FirstName = vm.FirstName,
...
};
if (vm.Programs != null)
{
vm.Programs.ForEach(consultant.Programs.Add);
}
...
return consultant;
}