【问题标题】:How can I update a Custom View Model using ASP MVC with DbContext如何使用带有 DbContext 的 ASP MVC 更新自定义视图模型
【发布时间】:2011-12-05 03:06:28
【问题描述】:

我有一个数据库模型,我想用多个视图对其进行编辑。我的实际数据库模型比我在这里展示的要大得多。

数据库模型: PersonModel

public class PersonModel {
    public Int32 Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Description { get; set; }
}

然后我有两个视图,EditPersonName.cshtmlEditPersonDescription.cshtml,使用户能够修改 PersonModel 的不同部分。

视图 1: EditPersonName.cshtml

@model EditPersonNameModel
@using (Html.BeginForm()) {
<div class="editor-label">@Html.LabelFor(m => m.FirstName)</div>
<div class="editor-field">@Html.TextBoxFor(m => m.FirstName)</div>
<div class="editor-label">@Html.LabelFor(m => m.LastName)</div>
<div class="editor-field">@Html.TextBoxFor(m => m.LastName)</div>
<input type="submit" value="Save" />
}

视图 2: EditPersonDescription.cshtml

@model EditPersonDescriptionModel
@using (Html.BeginForm()) {
<div class="editor-label">@Html.LabelFor(m => m.Description)</div>
<div class="editor-field">@Html.TextBoxFor(m => m.Description)</div>
<input type="submit" value="Save" />
}

我希望将它们分别链接到 EditPersonNameModelEditPersonDescriptionModel,而不是将每个视图直接绑定到 PersonModel

查看模型 1: EditPersonNameModel

public class EditPersonNameModel {
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

查看模型 2: EditPersonDescriptionModel

public class EditPersonDescriptionModel {
    public string Description { get; set; }
}

我想值得一提的是,我正在使用 EF 的 DbContext 模式。

public class PersonDbContext : DbContext {
    public DbSet<PersonModel> Persons { get; set; }
}

现在是我的控制器,事情开始崩溃了!

控制器: PersonController

public class PersonController {
    private PersonDbContext = new PersonDbContext();

    [HttpGet]
    public ActionResult EditName(int id) {
        // ??? ---- #1 -----
    }

    [HttpPost]
    public ActionResult EditName(EditPersonNameModel model, int id) {
        if (ModelState.IsValid) {
            // ??? ---- #2 ----- ??? Update FirstName,LastName ONLY
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(model);
    }

    [HttpGet]
    public ActionResult EditDescription(int id) {
        // ??? ---- #3 -----
    }

    [HttpPost]
    public ActionResult EditDescription(EditPersonDescriptionModel model, int id) {
        if (ModelState.IsValid) {
            // ??? ---- #4 ----- ??? Update Description ONLY
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(model);
    }

}

我的问题是我不知道在上面的 ??? ---- #X ----- 块中放什么。显然#1#3 将非常相似,#2#4 也是如此。

有人建议我使用AutoMapper。这有助于从 Db 模型到视图模型,但反之则不行。

目前我的#1 代码块如下所示:

[HttpGet]
public ActionResult EditName(int id) {
    PersonModel person = db.Persons.Find(id);
    if (person == null) {
        return HttpNotFound();
    }
    EditPersonNameModel viewModel;
    AutoMapper.Mapper.CreateMap<PersonModel, EditPersonNameModel>();
    viewModel = AutoMapper.Mapper.Map<PersonModel, EditPersonNameModel>(person);
    return View(viewModel);
}

我的#2 代码块如下所示:

[HttpPost]
public ActionResult EditName(EditPersonNameModel viewModel, int id) {
    if (ModelState.IsValid) {
        PersonModel person = db.Persons.Find(id);
        if (person == null) {
            return HttpNotFound();
        }
        EditPersonNameModel viewModel;
        AutoMapper.Mapper.CreateMap<EditPersonNameModel, PersonModel>();
        person = AutoMapper.Mapper.Map<EditPersonNameModel, PersonModel>(viewModel);
        db.Entry(person).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(viewModel);
}

#1 上面实际上工作正常。 AutoMapper 将从较大的 Person 对象映射到 EditPersonName 对象。

但是,上面的

#2 已损坏。 AutoMapper 将从 EditPersonNameModel 对象映射到一个新的 Person 对象,因此db.Entry(person).State = EntityState.Modified 将抛出异常:

ObjectStateManager 中已存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象。

我不需要 AutoMapper 来创建新对象,我需要映射到 现有 对象。要是有AutoMapper.Mapper.更新&lt;EditPersonNameModel, PersonModel&gt;(viewModel, person)就好了。

好的,现在这是一个很长的问题!感谢您的阅读,我非常感谢您的帮助!如果您熟悉 AutoMapper 并且知道我做错了什么,请告诉我。如果您阅读了此代码并知道完全不同的方法,请也告诉我!

谢谢!!!


更新 1

我有一个 #2 工作,但我不确定它有多好。

#2

[HttpPost]
public ActionResult EditName(EditPersonNameModel viewModel, int id) {
    if (ModelState.IsValid) {
        PersonModel person = db.Persons.Find(id);
        if (person == null) {
            return HttpNotFound();
        }
        TryUpdateModel(person);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(viewModel);
}

请注意,TryUpdateModel 是在 person 而不是 viewModel 上调用的。我已经从数据库中获取了person,所以它已经完全填满了,带有一个 ID。 TryModelUpdate 方法使用IModelBinder 将属性从当前“mvc 请求上下文”映射到指定对象。因为我的PersonModelEditPersonNameModel 使用相同的命名属性,所以它可以工作。这确实是一个非常糟糕的解决方案,所以如果您知道更好的解决方案,请告诉我!

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-3


    【解决方案1】:

    我建议使用Automapper 或类似的解决方案,在 mvc 3 中没有对模型->模型映射的内置支持。

    【讨论】:

    • AutoMapper 似乎不起作用。我会用一些例子来编辑我的帖子。
    • 我已经发布了我的新代码。如果你们中的任何一个都知道让 AutoMapper 工作的方法,那就太好了!
    • Mapper.Map 还接受两个对象 - 源和目标,因此您可以只使用 Mapper.Map(viewModel, person),它将使用 person 实例作为目标。
    • 好的,当我编辑我的帖子时显然已经很晚了。我应该注意到Map 的第二个参数! :) 这样可行!我仍然想知道是否有比我这样做更好的方法来做到这一点,但我将结束这个问题,并为此提出另一个更准确的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    • 1970-01-01
    • 2012-12-09
    • 2023-03-18
    • 2018-01-11
    • 2013-01-21
    • 1970-01-01
    相关资源
    最近更新 更多