asp.net - 尝试将 AutoMapper 用于具有子集合的模型,在 Asp.Net MVC 3 中出现空错误

标签 asp.net collections asp.net-mvc-3 null automapper

我是 AutoMapper 的新手,我有一个如下所示的 View :

@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>

这是 GET Edit 方法:
    public ActionResult Edit(int id)
    {
        Consultant consultant = _repository.GetConsultant(id);
        ConsultantViewModel vm = Mapper.Map<Consultant, ConsultantViewModel>(consultant);
        return View(vm);
    }

和 POST Edit 方法:
    [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");

    }

现在,当 AutoMapper 创建 ViewModel 时,它似乎工作正常(使用其最简单的形式,没有解析器或任何东西,只是将 Consultant 映射到 ConsultantViewModel),包括子集合等等。此外,还有 UserName 属性。现在,在 View 中我没有 UserName 字段,因为它总是由当前用户 (User.Identity.Name) 自动填充。但是当我取回 vm 时,UserName 属性为空,大概是因为 View 中没有它的字段。

我怀疑某些集合会导致相同的错误,即使我在其中为 UserName 放置了一个隐藏字段,因为顾问填写语言等是可选的......所以即使 ViewModel 有每个的实例化列表这些子集合进入 View (计数为 0),它们返回一个空值。

我该如何解决这个问题?我不想强制用户在所有子集合中填充值。我的意思是,我总是可以为 Name 属性创建一个带有空字符串的 Language 对象,但这意味着不必要的额外代码,而我真正想要的只是让子集合(和用户名)恢复到它们进入查看 - 填写 UserName 并实例化子集合,但如果用户未添加任何项目,则计数为 0。

更新:

我不知道,我想我以某种方式误解了 AutoMapper ......我发现实际上子集合在映射方面并不是真正的问题,它可以很好地将它映射回顾问对象。但是...我还需要将 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");

    }

我究竟做错了什么?如何将 ViewModel 中的填充值返回到 Consultant 对象并将其保存到数据库中???

更新 2:

好吧,彻底糊涂了......重新开始:这是Application_Start中的 map 创建:
        Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember("Id", opts => opts.Ignore());
        Mapper.CreateMap<Consultant, ConsultantViewModel>();

编辑方法:
    // 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");
    }

这也不起作用,但显然至少尝试更新实体模型,因为现在我得到一个异常:

无法初始化 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:            }

这与我尝试将实体对象直接用作模型而不是让 AutoMapper 创建 ViewModel 时遇到的错误几乎相同。那么我做错了什么?这真让我抓狂...

更新 3:

嗯,永无止境的故事......我发现了一些关于在 AutoMapper 的 CreateMap 方法上使用 UseDestinationValue 的信息。所以我试过了,嗯,这实际上让我更进一步。但是...现在我在 SaveChanges() 上得到一个新的异常(在 EF 模型中)。现在的异常(exception)是:“操作失败:无法更改关系,因为一个或多个外键属性不可为空。”这似乎是一个异常(exception),如果您没有级联删除集,则在尝试删除一对多关系中的子对象时也会发生这种情况,但这不是我在这里尝试做的......

这是更新后的 CreateMap 方法:
Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember("Id", opts => opts.Ignore()).ForMember(
                x => x.Programs, opts => opts.UseDestinationValue());
            Mapper.CreateMap<Consultant, ConsultantViewModel>();

有任何想法吗?

最佳答案

还没有得到任何答案,我实际上找到了一种方法来使它工作。还是感觉不太好,因为代码有点冗长……所以如果有人有更好的想法,请提出来!

我更改了它,以便我现在为子集合中的每个类型都有一个 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;
    }

不同之处在于我通过 UpdateModel() 填充了 Consultant 的简单属性,然后遍历 ProgramDTO 的集合并映射每个 Program。

嗯,这有效,虽然我不太喜欢代码......在我完成此操作后它也击中了我,我还需要能够删除用户已“标记为删除”的任何项目可以这么说在 View 中。我正在使用一个 View ,您可以按照 Steven Sanderson 的教程通过 jQuery 添加和删除文本框等。但这使代码变得更加复杂......无论如何,这是我能想到的最好的,所以如果你能改进这个,请再次提供任何其他想法!我特别希望有一个解决方案,我不必在 POST 上手动循环遍历集合,而是让 AutoMapper 自己处理嵌套集合,而不会出现上述错误!

关于asp.net - 尝试将 AutoMapper 用于具有子集合的模型,在 Asp.Net MVC 3 中出现空错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5126603/

相关文章:

asp.net 菜单控件在 Safari 中无法正确呈现

asp.net - asp.net中的 session 注销

c# - 具有序列 ID 的线程安全固定大小循环缓冲区

asp.net-mvc-3 - 使用 onlyIf 进行验证不起作用

asp.net - 循环通过repeater控件来获取asp.net中Textbox的值

c# - 员工 ID 的标准正则表达式

c# - 如何在 C# 中将递归对象转换为集合?

java - 竞争激烈的消费者-生产者

javascript - MVC3 - 如何在 JavaScript 中动态设置 facebook 应用程序 ID?

c# - 使用 MVC 3 Entity Framework 代码优先,如何在一对多关系中创建一个新的 "many"对象?