MVC 5通过重用EF生成的代码使用ViewModel进行编辑

Boj*_*les 2 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);
}
Run Code Online (Sandbox Code Playgroud)

现在,MVC 5中自动生成的代码如下所示,我已将此Action修改为仅包含来自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);
}
Run Code Online (Sandbox Code Playgroud)

我有Get部分工作,我的模型视图正在返回:

return View(new EditBusinessViewModel(business));
Run Code Online (Sandbox Code Playgroud)

但是当我回发时,我在这一行上收到错误:

db.Entry(business).State = EntityState.Modified;
Run Code Online (Sandbox Code Playgroud)

实体类型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);
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以从View Model中的视图回发我需要的数据,通过匹配的ID找到数据库中的实体,并用EF scaffold代码更新它.

有没有更好的办法?

Mab*_*lah 5

好吧,您将无法使用当前的代码,原因我相信您在问题本身中指出了这一点.您正在使用两种不同的类型,一种是从数据库表映射的,另一种是您专门用于视图但未映射的类型.你的实体模型,你没有说哪个版本的EF,但是对于MVC 5,我认为它是6或6.1.

所以你有EF文本模板生成的实体POCO,你有ViewModel.即使属性相同,EF也不会采用您的ViewModel类型,因为它在edmx中没有映射定义,这就是它说它不在您已经识别的当前上下文中的原因.

但是,有一些不错的方法可以在这个系统中工作.如果你想使用单独的实体和ViewModel,我个人在我自己的大部分代码中都会这样做.你可以 :

  1. 这似乎是你有一个ID,如果ID指向对EF型号的唯一ID,您可以与该ID的实体做一个查找表,然后从您的视图模型的数值更新的实体的值,然后保存使用StateModified而不是ViewModel的实体.
  2. 如果属性完全相同或非常相似,在Model和ViewModel之间,您可以查看类似AutoMapper,https://github.com/AutoMapper/AutoMapper的内容,这样您就可以将ViewModel直接映射到实例您的实体模型类型.
  3. 如果你的Model和ViewModel有很大的不同,你可以构建一个静态转换,不知道有多少人这样做但我喜欢它们.基本上,您定义了两个静态方法,使您可以将模型转换为ViewModel,反之亦然.您可以在任何地方获益,您可以调用一种方法,如果任一类型的结构发生变化,您只需在一个位置更新它.
  4. 你说MVC 5中的自动生成代码,你可能只是EF 5附带的默认示例代码,但我认为你在谈论MVC 5脚手架. http://www.asp.net/visual-studio/overview/2013/aspnet-scaffolding-overview ; 如果是这样,那么这些代码的代码至少不需要在Controller端进行太多的改动,除非你有专门的域逻辑,它看起来并不像你那样.如果你想使用单独的ViewModel,我想你可以结合上面的一个建议,但Scaffolding的目的是删除在为基本CRUD方法公开DB模型时你必须做的大部分管道.

如果我错过了你想要的标记,请在评论中回复.此外,如果没有看到两个模型的类定义,为上述建议提供代码示例有点困难.如果您认为一个适合您的用例,我认为描述应该足够了吗?但是,如果您想要一些简单的代码示例,请使用这些类的代码更新您的答案,我可以提供一些.