And*_*ers 6 asp.net collections null automapper asp.net-mvc-3
我是AutoMapper的新手,我有一个看起来像这样的视图:
@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Consultant</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
@Html.TextAreaFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
Program du behärskar:
</div>
<div>
<table id="programEditorRows">
<tr>
<th>
Program
</th>
<th>
Nivå
</th>
</tr>
@foreach (var item in Model.Programs)
{
Html.RenderPartial("ProgramEditorRow", item);
}
</table>
<a href="#" id="addProgram">Lägg till</a>
</div>
<div class="editor-label">
Språk du behärskar:
</div>
<div>
<table id="languageEditorRows">
<tr>
<th>
Språk
</th>
<th>
Nivå
</th>
</tr>
@foreach (var item in Model.Languages)
{
Html.RenderPartial("LanguageEditorRow", item);
}
</table>
<a href="#" id="addLanguage">Lägg till</a>
</div>
<div>
<table id="educationEditorRows">
<tr>
<th>
Utbildning
</th>
<th>
Nivå
</th>
</tr>
@foreach (var item in Model.Educations)
{
Html.RenderPartial("EducationEditorRow", item);
}
</table>
<a href="#" id="addEducation">Lägg till</a>
</div>
<div>
<table id="workExperienceEditorRows">
<tr>
<th>
Arbetserfarenhet
</th>
<th>
Startdatum
</th>
<th>
Slutdatum
</th>
</tr>
@foreach (var item in Model.WorkExperiences)
{
Html.RenderPartial("WorkExperienceEditorRow", item);
}
</table>
<a href="#" id="addWorkExperience">Lägg till</a>
</div>
<div>
<table id="competenceAreaEditorRows">
<tr>
<th>
Kompetensområde
</th>
<th>
Nivå
</th>
</tr>
@foreach (var item in Model.CompetenceAreas)
{
Html.RenderPartial("CompetenceAreaEditorRow", item);
}
</table>
<a href="#" id="addCompetenceArea">Lägg till</a>
</div>
<div>
<input id="fileInput" name="FileInput" type="file" />
</div>
<p>
<input type="submit" value="Spara" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Run Code Online (Sandbox Code Playgroud)
这是GET Edit方法:
public ActionResult Edit(int id)
{
Consultant consultant = _repository.GetConsultant(id);
ConsultantViewModel vm = Mapper.Map<Consultant, ConsultantViewModel>(consultant);
return View(vm);
}
Run Code Online (Sandbox Code Playgroud)
和POST编辑方法:
[HttpPost]
[ValidateInput(false)] //To allow HTML in description box
public ActionResult Edit(int id, ConsultantViewModel vm, FormCollection collection)
{
Consultant consultant = Mapper.Map<ConsultantViewModel, Consultant>(vm);
_repository.Save();
return RedirectToAction("Index");
}
Run Code Online (Sandbox Code Playgroud)
现在,当AutoMapper创建ViewModel时,它似乎工作正常(使用其最简单的形式,没有解析器或任何东西,只是将Consultant映射到ConsultantViewModel),包括子集合和所有.此外,UserName属性在那里.现在,在视图中我没有UserName的字段,因为它总是由当前用户(User.Identity.Name)自动填充.但当我收回vm时,UserName属性为null,可能是因为View中没有该字段.
我怀疑一些集合会导致相同的错误,即使我在其中为UserName放置一个隐藏字段,因为它是可选的,以便顾问填写语言等...所以即使ViewModel有一个实例化的列表,每个这些子集合进入View(计数为0),它们返回时返回null值.
我该如何解决这个问题?我不想强迫用户在所有子集合中填充值.我的意思是,我总是可以为Name属性创建一个带有空字符串的Language对象,但这意味着不必要的额外代码,我真正想要的是让孩子集合(和UserName)回到他们进入视图 - 填写UserName,并且实例化子集合,但如果用户未添加任何项目,则计数为0.
更新:
我不知道,我认为我在某种程度上误解了AutoMapper ......我发现实际上子集合在映射方面确实不是问题,它可以很好地将它映射回Consultant对象.但是......我还需要将id放回Consultant对象中,因为ViewModel没有.但即便如此,当我保存到存储库时,它也不会被保存.这就是我认为我误解了AutoMapper的地方 - 我曾经以为它会以某种方式用ViewModel中的值填充Consultant对象,但我想它会导致顾问变量引用Map()语句中的不同对象?因为它都不存在......
这是修改后的POST方法(不起作用):
[HttpPost]
[ValidateInput(false)] //To allow HTML in description box
public ActionResult Edit(int id, ConsultantViewModel vm, FormCollection collection)
{
vm.UserName = User.Identity.Name;
Consultant consultant = _repository.GetConsultant(id);
consultant = Mapper.Map<ConsultantViewModel, Consultant>(vm);
consultant.Id = id;
_repository.Save();
return RedirectToAction("Index");
}
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?如何将ViewModel中的填充值返回到Consultant对象并将其持久保存到数据库?
更新2:
好吧,彻底迷茫...开始有点:这是Application_Start中的地图创建:
Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember("Id", opts => opts.Ignore());
Mapper.CreateMap<Consultant, ConsultantViewModel>();
Run Code Online (Sandbox Code Playgroud)
编辑方法:
// GET: /Consultant/Edit/5
public ActionResult Edit(int id)
{
Consultant consultant = _repository.GetConsultant(id);
ConsultantViewModel vm = Mapper.Map<Consultant, ConsultantViewModel>(consultant);
return View(vm);
}
//
// POST: /Consultant/Edit/5
[HttpPost]
[ValidateInput(false)] //To allow HTML in description box
public ActionResult Edit(int id, ConsultantViewModel vm, FormCollection collection)
{
vm.UserName = User.Identity.Name;
Consultant consultant = _repository.GetConsultant(id);
consultant = Mapper.Map<ConsultantViewModel, Consultant>(vm, consultant);
_repository.Save();
return RedirectToAction("Index");
}
Run Code Online (Sandbox Code Playgroud)
这也不起作用,但显然至少会尝试更新实体模型,因为现在我得到一个例外:
无法初始化EntityCollection,因为EntityCollection所属对象的关系管理器已附加到ObjectContext.只应调用InitializeRelatedCollection方法在对象图的反序列化期间初始化新的EntityCollection.
和YSOD错误代码示例:
Line 698: if ((value != null))
Line 699: {
Line 700: ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Program>("ConsultantsModel.ConsultantProgram", "Program", value);
Line 701: }
Line 702: }
Run Code Online (Sandbox Code Playgroud)
这与我在尝试直接使用实体对象作为模型时获得的错误相同,而不是让AutoMapper创建ViewModel.那么我做错了什么?这真让我抓狂...
更新3:
好吧,无尽的故事......我在AutoMapper的CreateMap方法中找到了一些使用UseDestinationValue的信息.所以我试过了,好吧,实际上让我更进一步.但是......现在我在SaveChanges()上获得了一个新的异常(在EF模型中).现在的例外是:"操作失败:由于一个或多个外键属性不可为空,因此无法更改关系." 这似乎是一个异常,如果您没有级联删除集,当尝试删除一对多关系中的子对象时也会发生异常,但这不是我在这里尝试做的...
这是更新的CreateMap方法:
Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember("Id", opts => opts.Ignore()).ForMember(
x => x.Programs, opts => opts.UseDestinationValue());
Mapper.CreateMap<Consultant, ConsultantViewModel>();
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
还没有得到任何答案,我实际上找到了一种让它发挥作用的方法。仍然感觉不太好,因为代码有点冗长......所以如果有人有更好的想法,请提出!
我对其进行了更改,以便现在子集合中的每种类型都有一个 DTO 对象(在使用 AutoMapper 时可能应该从该对象开始)。例如,我现在有一个 ProgramDTO 类型来与 Program 进行映射。
我尝试简单地使用 Consultant 对象进行映射,希望嵌套集合能够自行解决,但只再次收到“EntityCollection 已初始化”错误。所以凭直觉我尝试了这个方法:
private Consultant CreateConsultant(ConsultantViewModel vm, Consultant consultant) //Parameter Consultant needed because an object may already exist from Edit method.
{
Mapper.Map(vm, consultant);
//To do this I had to add an Ignore in the mapping configuration:
//Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember(x => x.Programs, opts => opts.Ignore());
//Delete items "marked for deletion" by removing with jQuery in the View:
var programs = consultant.Programs.Except(consultant.Programs.Join(vm.Programs, p => p.Id, d => d.Id, (p, d) => p)).ToList();
Delete(programs);
foreach (var programDto in vm.Programs)
{
Program program = consultant.Programs.SingleOrDefault(x => x.Id == programDto.Id);
if (program == null)
{
program = new Program();
consultant.Programs.Add(program);
}
program = Mapper.Map(programDto, program);
}
_repository.Save();
return consultant;
}
Run Code Online (Sandbox Code Playgroud)
不同之处在于,我通过 UpdateModel() 填充 Consultant 的简单属性,然后迭代 ProgramDTO 集合并映射每个 Program。
好吧,这很有效,尽管我不太喜欢这段代码...完成此操作后,我还需要能够删除用户“标记为删除”的任何项目,可以这么说在视图中。我正在使用一个视图,您可以按照 Steven Sanderson 的教程通过 jQuery 添加和删除文本框等。但这使代码变得更加复杂......无论如何,这是我能想到的最好的,所以如果您可以对此进行改进,请再次提供任何其他想法!我特别希望有一个解决方案,我不必在 POST 上手动循环集合,而是让 AutoMapper 自行处理嵌套集合,而不会出现上述错误!
归档时间: |
|
查看次数: |
4330 次 |
最近记录: |