尝试使用AutoMapper与孩子集合的模式,让零错误Asp.Net MVC 3 [英] Trying to use AutoMapper for model with child collections, getting null error in Asp.Net MVC 3

查看:390
本文介绍了尝试使用AutoMapper与孩子集合的模式,让零错误Asp.Net MVC 3的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是完全新的AutoMapper,我有一个观点,看起来像这样:

I'm completely new to AutoMapper, and I have a View that looks like this:

@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方法:

Here's the GET Edit method:

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

而POST Edit方法:

And the POST Edit method:

    [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创建视图模型似乎是工作的罚款(用最简单的形式,没有旋转变压器或什么,只是映射顾问ConsultantViewModel),包括儿童藏品和所有。此外,UserName属性是存在的。现在,在查看我没有用户名的字段,因为它总是自动被当前用户(User.Identity.Name)填写。但是,当我得到的虚拟机背,UserName属性为null,presumably因为当时在查看它没有现场。

Now, when AutoMapper creates the ViewModel it seems to be working fine (using its simplest form, no resolver or anything, just mapping Consultant to ConsultantViewModel), including the child collections and all. Also, the UserName property is there. Now, in the View I don't have a field for UserName, because it is always automatically filled in by the current User (User.Identity.Name). But when I get the vm back, the UserName property is null, presumably because there was no field for it in the View.

我怀疑有些藏品会导致同样的错误,即使我把一个隐藏字段中有用户名,因为它是可选的顾问在语言等填补。所以,即使在ViewModel有一个实例列表为每个孩子藏品将在该视图(为0的计数),他们回来用null值代替。

I suspect some of the collections will cause the same error even if I put a hidden field in there for UserName, because it is optional for the Consultant to fill in Languages etc... So even though the ViewModel has an instantiated list for each of these child collections going in to the View (with a count of 0), they come back with a null value instead.

我该如何解决这个问题?我不希望强制具有填充在由用户的所有子集的值。我的意思是,我总是可以创建具有Name属性为空字符串的语言对象,但是这将意味着不必要的额外code,和我真正想要的是让孩子集合(和用户名)回他们去的路上到视野 - 与用户名填充,并用子集被实例化,但是具有0的计数,如果用户还没有添加任何项

How do I solve this? I don't want to force having values filled in all the child collections by the user. I mean, I could always create a Language object with an empty string for the Name property, but that would mean unnecessary extra code, and all I really want is to get the child collections (and UserName) back the way they went in to the View - with UserName filled in, and with the child collections being instantiated, but with a count of 0 if the user hasn't added any items.

更新:

我不知道,我觉得我莫名其妙地误解AutoMapper ......我发现,其实孩子集合关于映射,即正常工作映射它放回咨询对象were'nt确实是个问题。不过......我也需要把ID回到顾问的对象,因为视图模型没有任何。但即便如此,当我保存到存储库,它不会被保存。这就是我想我误解AutoMapper - 我原以为这会在一定程度填充从视图模型值顾问的对象,但我想它会导致顾问变量引用不同的对象,在地图()语句?因为它都没有坚持......

I don't know, I think I'm misunderstanding AutoMapper somehow... I found that in fact the child collections were'nt really a problem with regard to the mapping, that worked fine mapping it back into the Consultant object. However...I also needed to put the id back in the Consultant object, because the ViewModel didn't have any. But even so, when I save to repository, it doesn't get saved. This is where I think I misunderstand AutoMapper - I had thought it would somehow populate the Consultant object with values from the ViewModel, but I guess it causes the consultant variable to refer to a different object in the Map() statement? Because none of it is persisted...

下面是修改后的POST方法(不工作):

Here's the modified POST method (which doesn't work):

    [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");

    }

我是什么做错了吗?我如何获得视图模型的填充值回顾问对象,其保存到数据库???

What am I doing wrong? How do I get the populated values in the ViewModel back into the Consultant object and persist it to the database???

更新2:

好吧,看一遍迷茫......从头开始一点:这是在的Application_Start地图创建:

Ok, thorougly confused... Starting over a bit: Here's the map creation in Application_Start:

        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");
    }

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

This doesn't work either, but apparently at least tries to update the entity model, because now I get an exception:

该EntityCollection无法初始化因为关系经理以该EntityCollection属于已经被附加到ObjectContext的对象。该InitializeRelatedCollection方法只应调用对象图的反序列化过程中初始化一个新的EntityCollection。

The EntityCollection could not be initialized because the relationship manager for the object to which the EntityCollection belongs is already attached to an ObjectContext. The InitializeRelatedCollection method should only be called to initialize a new EntityCollection during deserialization of an object graph.

和YSOD错误code样品:<​​/ P>

And YSOD error code sample:

Line 698:                if ((value != null))
Line 699:                {
Line 700:                    ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Program>("ConsultantsModel.ConsultantProgram", "Program", value);
Line 701:                }
Line 702:            }

这是pretty多尝试直接使用实体对象为模型,而不是AutoMapper创建一个视图模型时,我得到了同样的错误。所以我在做什么错了?这是推动我疯了...

This is pretty much the same error I got when trying to use the entity object directly as model, rather than having AutoMapper create a ViewModel. So what am I doing wrong? This is driving me crazy...

更新3:

好了,无休止的故事......我发现在AutoMapper的CreateMap方法使用UseDestinationValue一些信息。所以,我试过了,和好,实际上让我远一点。但是......现在我得到的SaveChanges上()(在EF模型)的新异常。唯一的例外是现在:操作失败:该关系不能被改变,因为一个或多个外键的属性是不可为空这似乎是试图删除一个一对多的关系的子对象,如果你没有级联删除集时也发生异常,但是这不是我想在这里做...

Well, neverending story... I found some info on using UseDestinationValue on the CreateMap method in AutoMapper. So I tried that, and well, that actually got me a bit further. But...now I get a new exception on SaveChanges() (in the EF model). The exception now is: "The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable." This appears to be an exception that also occurs when trying to delete child objects in a one-to-many relationship if you don't have cascade delete set, but that's not what I'm trying to do here...

下面是更新CreateMap方法:

Here's the updated CreateMap methods:

Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember("Id", opts => opts.Ignore()).ForMember(
                x => x.Programs, opts => opts.UseDestinationValue());
            Mapper.CreateMap<Consultant, ConsultantViewModel>();

任何想法?

推荐答案

还没有得到任何解答,我居然找到了一种方法,使其工作。仍然感觉并不好,因为code是那种冗长......因此,如果任何人有更好的想法,请把他们!

Haven't gotten any answers yet, and I actually found a way to make it work. Still doesn't feel good, because the code is kind of verbose... So if anyone has better ideas, please bring them!

我改变它,这样我现在已经为每个子集的类型的DTO对象(大概应该有这样的开始使用AutoMapper时)。因此,例如我现在有一个ProgramDTO类型映射与计划。

I changed it so that I now have a DTO object for each of the types in the child collections (probably should have had that to begin with when using AutoMapper). So for instance I now have a ProgramDTO type to map with Program.

我试图与咨询对象只是做了映射,希望能为嵌套集合到自己工作了,但只有一次拿到EntityCollection已经被初始化的错误。于是就预感这个方法我试过来代替:

I tried doing the mapping simply with the Consultant object, hoping for the nested collections to work themselves out, but only got the "EntityCollection has already been initialized" error again. So on a hunch I tried this method instead:

    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(),然后通过ProgramDTOs集合迭代,每个节目映射起来。

The difference being that I fill the simple properties of Consultant by UpdateModel(), and then iterate through the collection of ProgramDTOs and map up each Program.

好这个工作,虽然我不喜欢code非常多......这还打我我已经做到了这一点后,我还需要能够删除用户已标记的任何项目删除,所以在查看说话。我使用的视图,您可以添加和的jQuery删除文本框等,之后由史蒂芬·桑德森的教程。不过本作的code更加复杂......无论如何,这是我能想到的最好的,所以再次请提供任何其他的想法,如果你能在这改善!我尤其会喜欢一个解决方案,我没有手动通过POST集合环,但AutoMapper处理嵌套藏品本身,没有上面提到的错误!

Well this worked, although I don't like the code very much... It also hit me after I had done this, that I also need to be able to delete any items that the user has "marked for deletion" so to speak in the View. I'm using a view where you can add and remove textboxes etc by jQuery, following a tutorial by Steven Sanderson. But this made the code even more complicated... Anyway, this was the best I could think of, so again please offer any other ideas if you can improve on this! I particularly would have liked a solution where I didn't have to manually loop through the collections on POST, but have AutoMapper handle the nested collections itself, without the errors mentioned above!

这篇关于尝试使用AutoMapper与孩子集合的模式,让零错误Asp.Net MVC 3的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆