Хелпер hiddenFor извлекает идентификатор из параметра действия, а не viewModel

Я наткнулся на действительно странную ситуацию с ASP.Net MVC, передавая "id" в качестве параметра действия и "id" скрытый элемент формы. У меня есть действие с параметром "id". Это значение идентификатора представляет проект. У меня есть действие контроллера, где я создаю форму ввода данных для назначения сотрудника переданному проекту (я создаю раскрывающийся список, в котором администратор выбирает сотрудника, который будет назначен для переданного проекта). Назначая сотрудника для проекта, я создаю запись ProjectEmployee (идентификатор проекта, идентификатор сотрудника и идентификатор, представляющий комбинацию, которая является столбцом идентификаторов в базе данных). Столбец идентификации (который называется "id") также является скрытым элементом формы. Это необходимо, потому что мне нужно иметь возможность редактировать проект / назначение сотрудника позднее.

В любом случае, при создании нового назначения сотрудника для проекта идентификатор проекта (то есть "идентификатор", передаваемый действию) применяется к скрытому элементу формы "id".

Я передаю 117 в действие. 117 - это идентификатор проекта. Это устанавливается в скрытое поле "id", которое должно быть 0, потому что 0 представляет новый проект / назначение сотрудника.

Посмотреть модель

id  - unique id that represents the Project/Employee combination
projectId - the "Id" being passed to action
EmployeeId - what the admin is selecting from drop down
rate
startdate
endDate
...

Форма ввода данных

@Html.HiddenFor(m => m.Id) 
@Html.HiddenFor(m => m.ProjectId)
...
Id: @Model.Id <br />
ProjectId: @Model.ProjectId<br />

Таким образом, @Html.HiddenFor(m => m.Id) отображает скрытый элемент формы со значением 117. @Model.Id отображает 0 для пользовательского интерфейса. Пройдя по коду, я могу визуально увидеть значение свойства Id равным 0. Почему HiddenFor пересекает свои провода и вытягивает значение 117?

Эта ошибка попала в производственную среду, поэтому у меня возникла путаница с запутанными данными, потому что вместо создания новой записи в таблице базы данных я фактически ОБНОВЛЯЮ существующие записи, потому что свойство "Id" ошибочно устанавливается от 0 (который представляет новую запись) до 117 (который является идентификатором проекта) и, следовательно, я обновляю другую запись.

2 ответа

Решение

Как получается, что HiddenFor пересекает провода и получает значение 117?

Это по замыслу. Все помощники HTML, такие как TextBoxFor и HiddenFor, сначала используют значение ModelState при связывании, а затем значение модели. Я предполагаю, что ваше действие контроллера выглядит так:

public ActionResult Foo(int id)
{
    SomeModel model = ...
    // At this stage model.Id = 0
    return View(model);
}

Дело в том, что связыватель модели по умолчанию добавляет значение в ModelState с ключом id который используется помощником, а значение свойства модели игнорируется. Это не ошибка. Так устроены помощники HTML. Вначале это немного сбивает с толку, когда вы этого не знаете, но как только вы привыкнете к такому поведению, вы не попадете в ловушку второй раз.

Одна возможность исправить проблему - удалить это значение из ModelState:

public ActionResult Foo(int id)
{
    ModelState.Remove("id");
    SomeModel model = ...
    // At this stage model.Id = 0
    return View(model);
}

Или переименуйте Id свойство внутри вашей модели, чтобы что-то другое, чтобы избежать конфликта.

В качестве альтернативы изменению имен ваших полей вы можете создать новый вспомогательный метод, который не использует значения маршрута при оценке того, что добавить в атрибут значения.

Я создал этот помощник для чрезвычайно распространенных id дело:

public static MvcHtmlString HiddenIdFor<TModel, TValue>(this HtmlHelper<TModel> html,
        Expression<Func<TModel, TValue>> expression)
    {
        var value = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Model.ToString();
        var builder = new TagBuilder("input");
        builder.MergeAttribute("type", "hidden");
        builder.MergeAttribute("name", ExpressionHelper.GetExpressionText(expression));
        builder.MergeAttribute("value", value);
        return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));

}

Тогда вы можете использовать это в шаблоне:

@Html.HiddenIdFor(m => m.Id) 
@Html.HiddenIdFor(m => m.ProjectId)
Другие вопросы по тегам