Попытка использовать 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>

Вот метод 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, похоже, он работает нормально (с использованием его простейшей формы, без преобразователя или что-нибудь, просто отображение Консультант по ConsultantViewModel), включая дочерние коллекции и все такое. Также есть свойство UserName. Теперь в представлении у меня нет поля для UserName, потому что оно всегда автоматически заполняется текущим пользователем (User.Identity.Name). Но когда я возвращаю виртуальную машину, свойство UserName имеет значение null, предположительно потому, что в представлении не было поля для него.

Я подозреваю, что некоторые коллекции вызовут ту же ошибку, даже если я добавлю туда скрытое поле для UserName, потому что Консультанту необязательно заполнять языки и т. Д.Таким образом, даже несмотря на то, что ViewModel имеет список экземпляров для каждой из этих дочерних коллекций, входящих в View (со счетчиком 0), вместо этого они возвращаются с нулевым значением.

Как мне решить эту проблему? Я не хочу заставлять пользователя заполнять значения всех дочерних коллекций. Я имею в виду, что я всегда мог создать объект Language с пустой строкой для свойства Name, но это означало бы ненужный дополнительный код, и все, что я действительно хочу, - это вернуть дочерние коллекции (и UserName) в том виде, в котором они вошли в View - с заполненным UserName и созданием экземпляров дочерних коллекций, но со счетом 0, если пользователь не добавил никаких элементов.

ОБНОВЛЕНИЕ:

Я не знаю, я думаю, что я неправильно понимаю AutoMapper ... Я обнаружил, что на самом деле дочерние коллекции не были проблемой в отношении сопоставления, которое отлично работало, отображая его обратно в объект Консультант. Однако ... мне также нужно было вернуть идентификатор в объект Консультанта, потому что в ViewModel его не было. Но даже в этом случае, когда я сохраняю в репозиторий, он не сохраняется. Здесь, я думаю, я неправильно понимаю AutoMapper - я думал, что он каким-то образом заполнит объект Consultant значениями из ViewModel, но я предполагаю, что это заставляет переменную консультанта ссылаться на другой объект в операторе 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 в объект Консультант и сохранить их в базе данных ???

ОБНОВЛЕНИЕ 2:

Хорошо, сильно запутались ...Начнем сначала: вот создание карты в 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");
    }

Это тоже не работает, но, по-видимому, по крайней мере пытается обновить модель сущности, потому что теперь я получаю исключение:

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:

Ну, бесконечная история ... Я нашел некоторую информацию об использовании UseDestinationValue в методе CreateMap в AutoMapper. Так что я попробовал это, и это действительно продвинуло меня немного дальше. Но ... теперь я получаю новое исключение в SaveChanges () (в модели EF). Исключением является следующее: «Операция завершилась неудачно: связь не может быть изменена, поскольку одно или несколько свойств внешнего ключа не допускают значения NULL». Похоже, что это исключение, которое также возникает при попытке удалить дочерние объекты в отношении «один ко многим», если у вас нет набора каскадного удаления, но это не то, что я пытаюсь сделать здесь ...

Вот обновленные методы CreateMap:

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

Есть идеи?

6
задан Anders 26 February 2011 в 19:03
поделиться