Хелпер 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)