Entity Framework TryUpdateModel Дочерние объекты? - PullRequest
3 голосов
/ 04 ноября 2011

Я знаю, что большинство из вас предложит мне использовать ViewModels, специфичные для формы, которую я использую, но мне любопытно, почему мой дочерний объект не связывается с TryUpdateModel.

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>User</legend>

        @Html.HiddenFor(model => model.UserId)
    @Html.HiddenFor(model => model.PrimaryAddress.AddressId)
    <div class="editor-label">
            @Html.LabelFor(model => model.PrimaryAddress.FirstName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.PrimaryAddress.FirstName)
            @Html.ValidationMessageFor(model => model.PrimaryAddress.FirstName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.PrimaryAddress.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.PrimaryAddress.LastName)
            @Html.ValidationMessageFor(model => model.PrimaryAddress.LastName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.UserName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.UserName)
            @Html.ValidationMessageFor(model => model.UserName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Email)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Email)
            @Html.ValidationMessageFor(model => model.Email)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.IsApproved)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.IsApproved)
            @Html.ValidationMessageFor(model => model.IsApproved)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.IsEmployee)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.IsEmployee)
            @Html.ValidationMessageFor(model => model.IsEmployee)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

И код контроллера:

[HttpPost]
public ActionResult Edit(int id, FormCollection form)
{
    var user = Token.DB.Users.Include("PrimaryAddress").Single(x => x.UserId == id);
    if (TryUpdateModel(user, new string[] { "UserName", "Email", "IsApproved", "IsEmployee", "PrimaryAddress.FirstName", "PrimaryAddress.LastName" }))
    {
        try
        {
            Token.DB.SaveChanges();
            return RedirectToAction("index");
        }
        catch (Exception ex)
        {
            while (ex.InnerException != null)
                ex = ex.InnerException;
            if (ex.Message.ToLowerInvariant().Contains("unique"))
                ModelState.AddModelError("UserName", "UserName already exists");
        }
    }
    return View(User);
}

Код не генерирует никаких исключений, он просто не заполняет user.PrimaryAddress.FirstName или user.PrimaryAddress.LastName из формы. Я хотел бы знать, почему?

Я уже знаю, что могу исправить проблему с помощью определенной ViewModel и отображения информации в фоновом режиме. Я также могу сделать что-то вроде этого:

<!-- Edit.cshtml -->
<div class="editor-field">
    @Html.EditorFor(model => model.PrimaryAddress.FirstName, null, "FirstName")
    @Html.ValidationMessageFor(model => model.PrimaryAddress.FirstName)
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.PrimaryAddress.LastName)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.PrimaryAddress.LastName, null, "LastName")
    @Html.ValidationMessageFor(model => model.PrimaryAddress.LastName)
</div>

// UsersController.cs
if (TryUpdateModel(user, new string[] { "UserName", "Email", "IsApproved", "IsEmployee"})
&& TryUpdateModel(user.PrimaryAddress, new string[] {"FirstName", "LastName" }))

Таким образом, реальный вопрос заключается в том, почему это не является обязательным в первом примере?

Ответы [ 2 ]

9 голосов
/ 04 ноября 2011

Таким образом, реальный вопрос заключается в том, почему это не является обязательным в первом примере?

Ответ на ваш вопрос очень прост: ни один из атрибутов UpdateModel, TryUpdateModel или [Bind] не поддерживает «вложенные свойства» в списке свойств включения / исключения. Так что делай вещи правильно и пользуйся моделями вида. Защита от массовых атак на присвоение свойств - это только одна из миллионов причин, по которым вам следует использовать модели представлений. Что ж, вы, похоже, нашли обходной путь , выполнив секунду TryUpdateModel, но если у вас есть много свойств для этого доменного объекта, ваш код действия контроллера может быстро превратиться в спагетти-код.

1 голос
/ 15 октября 2012

Я нахожу ряд случаев, когда я хочу сделать то же самое.Я также обнаружил, что дочерние объекты не работают, но списки / коллекции дочерних объектов работают очень странно.

Я нашел хорошую ссылку , описывающую, как обойти это.

В конечном итоге это выглядит примерно так:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(int id, FormCollection collection)
{
    User user = null;
    if (id == 0)
    {
        user = new User();
        UpdateModel(user, "User");
        user.Contact = new Contact();
        UpdateModel(user.Contact, "User.Contact");
        user.Contact.Addresses = new EntitySet<Address>();
        UpdateModel(user.Contact.Addresses, "User.Contact.Addresses");
    }
    else
    {
        // get current user object from DB, however you normally do this is fine.
        user = userRepository.GetById(id);
        UpdateModel(user, "User");
        UpdateModel(user.Contact, "User.Contact");
        UpdateModel(user.Contact.Addresses, "User.Contact.Addresses");
    }
    // at this point, model "user" and children would have been updated.
}
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...