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

时间:2014-06-19 04:59:01

标签: 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中自动生成的代码如下所示,我已将此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);
}

获取部分正常工作,我的模型查看正在返回:

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);
}

这样我就可以从视图模型中的视图回发我需要的数据,通过匹配的ID找到数据库中的实体,并用EF脚手架代码更新它。

有更好的方法吗?

2 个答案:

答案 0 :(得分:5)

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

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

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

  1. 看起来您有一个ID,如果该ID指向EF模型上的唯一ID,您可以查找具有该ID的实体,然后使用ViewModel中的值更新实体的值然后使用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模型时你必须做的大部分管道。
  5. 如果我错过了您要找的内容,请在评论中回复。此外,如果没有看到两个模型的类定义,为上述建议提供代码示例有点困难。如果您认为一个适合您的用例,我认为描述应该足够了吗?但是,如果您想要一些简单的代码示例,请使用这些类的代码更新您的答案,我可以提供一些。

答案 1 :(得分: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 ,而非实体