#entity-framework #asp.net-mvc-5
#entity-framework #asp.net-mvc-5
Вопрос:
У меня есть бизнес-модель и EditBusinessViewModel.
В MVC 4 я бы использовал примерно такой код для редактирования записи:
[HttpPost]
public ActionResult Edit(MainMenu mainmenu)
{
if (ModelState.IsValid)
{
db.MainMenus.Attach(mainmenu);
db.ObjectStateManager.ChangeObjectState(mainmenu, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mainmenu);
}
Теперь автоматически сгенерированный код в MVC 5 выглядит следующим образом, я изменил это действие, чтобы включать только поля из моей EditBusinessViewModel и назвал его Edit2:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit2([Bind(Include = "ID,BusinessName,BusinessDescription,BusinessAddress,BusinessPhoneOne,BusinessPhoneTwo,BusinessWeb,BusinessEmail,BusinessMelRef")] EditBusinessViewModel business)
{
if (ModelState.IsValid)
{
db.Entry(business).State = EntityState.Modified;
db.SaveChanges();
return Redirect("~/Home/Index/" business.ID);
}
return View(business);
}
У меня работает часть Get, моя модель и представление работают, возвращая:
return View(new EditBusinessViewModel(business));
Но когда я отправляю ответ, я получаю сообщение об ошибке в этой строке:
db.Entry(business).State = EntityState.Modified;
Тип объекта EditBusinessViewModel не является частью модели для текущего контекста.Я полагаю, что это не так и причина ViewModel?
Что я хотел бы знать, могу ли я использовать этот код или есть что-то еще, что я должен делать?
Обновить
Я слишком много думал об этом, и ViewModel — это просто ViewModel, так что теперь у меня есть:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit2([Bind(Include = "ID,BusinessDescription,BusinessAddress,BusinessPhoneOne,BusinessPhoneTwo,BusinessWeb,BusinessEmail,BusinessMelRef")] EditBusinessViewModel business)
{
if (ModelState.IsValid)
{
business.UserEmail = User.Identity.GetUserName();
Business newbus = db.Businesses.Find(business.ID);
{
newbus.BusinessDescription = business.BusinessDescription;
newbus.BusinessAddress = business.BusinessAddress;
};
db.Entry(newbus).State = EntityState.Modified;
db.SaveChanges();
return Redirect("~/Home/Index/" business.ID);
}
return View(business);
}
Таким образом, я отправляю обратно нужные мне данные из представления в модели представления, нахожу объект в базе данных по его совпадающему идентификатору и обновляю его с помощью кода каркаса EF.
Есть ли лучший способ?
Комментарии:
1. Я думаю, что ваш обновленный способ — очень разумный способ сделать это. Модели представления обеспечивают хорошую абстракцию, вы платите небольшие накладные расходы, когда вам приходится искать модель из контекста вашей БД, но в общей схеме я обнаружил, что для многих случаев использования это стоит того, чтобы изолировать ваши модели от внешнего интерфейса.
2. Отличный ответ и большое спасибо за вашу помощь и время! майк.
Ответ №1:
Ну, вы не сможете использовать свой текущий код по причинам, на которые, я полагаю, вы указываете в самом своем вопросе. Вы работаете с двумя разными типами, один из которых отображается из таблицы БД, а другой используется специально для ваших представлений и не отображается. В вашей Entity Model вы не указываете, какая версия EF, но с MVC 5 я предполагаю, что это 6 или 6.1.
Итак, у вас есть POCO вашей сущности, созданный текстовым шаблоном EF, и у вас есть ViewModel. Даже если бы свойства были идентичны, EF не принял бы ваш тип ViewModel, потому что у него нет определения сопоставления в edmx, по этой причине он говорит, что его нет в текущем контексте, как вы уже поняли.
Однако в этой системе есть несколько достойных способов работы. ЕСЛИ вы хотите использовать отдельные объекты и ViewModels, что я лично делаю в большинстве своих собственных кодов. Вы могли бы :
- Похоже, у вас есть идентификатор, если этот идентификатор указывает на уникальный идентификатор в модели EF, вы можете выполнить поиск объекта с этим идентификатором, а затем обновить значения объекта значениями из вашей ViewModel, а затем сохранить объект с StateModified вместо ViewModel.
- Если свойства точно такие же или очень похожи, между вашей моделью и ViewModel вы можете посмотреть что-то вроде AutoMapper, https://github.com/AutoMapper/AutoMapper , что позволит вам сопоставить вашу ViewModel непосредственно с экземпляром вашего типа модели сущности.
- Если ваша модель и ViewModel сильно отличаются, вы можете создать статическую форму, не уверен, сколько людей это делают, но они мне нравятся. По сути, вы определяете два статических метода, которые позволяют преобразовать вашу модель в вашу ViewModel и наоборот. Преимущество в том, что в любом месте, где вам нужно это сделать, вы можете вызвать один метод, и если структура любого типа изменится, вам просто нужно обновить это в одном месте.
- Вы говорите, что автоматически созданный код в MVC 5, вы можете иметь в виду только пример кода по умолчанию, который поставляется с EF 5, но я думаю, что вы говорите о строительных лесах MVC 5. http://www.asp.net/visual-studio/overview/2013/aspnet-scaffolding-overview ; если это так, то код для них не должен нуждаться в значительных изменениях, по крайней мере, на стороне контроллера, если только у вас нет специальной логики домена, которая выглядит не так, как у вас. Если вы хотите использовать отдельные ViewModels, я полагаю, вы могли бы использовать в сочетании с одной из приведенных выше рекомендаций, но смысл создания лесов состоит в том, чтобы удалить большую часть сантехники, которую вам приходится выполнять при предоставлении моделей БД для базовых методов CRUD.
Если я пропустил отметку о том, что вы ищете, пожалуйста, ответьте в комментарии. Кроме того, немного сложно предоставить примеры кода для приведенных выше рекомендаций, не видя определений классов для ваших двух моделей. Я думаю, описаний должно быть достаточно, чтобы уйти, если вы считаете, что оно хорошо подойдет для вашего варианта использования? Но, если вы хотите несколько простых примеров кода, обновите свой ответ кодом для этих классов, и я могу предоставить некоторые.
Ответ №2:
Из вашего опубликованного фрагмента:
return View(new EditBusinessViewModel(business));
Здесь business
не ваша ViewModel, а переменная (предположительно, ваша сущность из БД), которая используется в конструкторе вашей ViewModel. Я могу только предположить, что он передан с намерением сохранить его в одном из свойств вашей ViewModel.
public ActionResult Edit2([Bind(Include = "...")] EditBusinessViewModel business)
Вот business
ваша ViewModel. EditBusinessViewModel
Как вы можете видеть, он имеет тип. Но в этом методе вы выполняете следующий вызов:
db.Entry(business).State = EntityState.Modified;
EditBusinessViewModel
не является типом, известным EF, поскольку это ваша viewmodel. Предполагается, что вы передаете свою сущность в базу данных. ViewModel следует использовать только в вашем проекте MVC.
Я почти уверен, что одно из свойств вашего EditBusinessViewModel
объекта — это объект, который вам нужен. Это смутно подтверждается тем фактом, что вы передаете свою сущность в EditBusinessViewModel
конструкторе.
Я не знаю, как называется свойство, поскольку вы не опубликовали класс ViewModel. Предполагая, что он вызван MyEntity
, это должно сработать:
db.Entry(business.MyEntity).State = EntityState.Modified;
Но для ясности я бы предложил переименовать этот параметр, чтобы избежать путаницы между отдельными видами использования business
переменной. Измените его на businessVM
или что-то подобное, чтобы вам всегда напоминали, что вы работаете с ViewModel, а не с сущностью.