C# 使用绑定在控制器中编辑模型会删除绑定中未包含的属性

C# 使用绑定在控制器中编辑模型会删除绑定中未包含的属性,c#,asp.net,asp.net-mvc,entity-framework,C#,Asp.net,Asp.net Mvc,Entity Framework,控制器上的以下代码用于编辑具有属性name、attr1、attr2、attr3等的模型MyClass,属性10。bind[bind(Include=“ID,name,attr1,attr2”)]不包括大多数属性,结果是在将更改保存到数据库后,这些属性被清除 我想要的是更新绑定的属性,但保留其余属性的旧值,而无需在绑定中指定它们(因为有很多属性,并且它们经常更改)。这是完整的编辑功能代码: [HttpPost] [ValidateAntiForgeryToken] public ActionRes

控制器上的以下代码用于编辑具有属性
name、attr1、attr2、attr3等的模型
MyClass
,属性10
。bind
[bind(Include=“ID,name,attr1,attr2”)]
不包括大多数属性,结果是在将更改保存到数据库后,这些属性被清除

我想要的是更新绑定的属性,但保留其余属性的旧值,而无需在绑定中指定它们(因为有很多属性,并且它们经常更改)。这是完整的编辑功能代码:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,name,attr1,attr2")] MyClass item)
{
    if (ModelState.IsValid)
    {
        db.Entry(item).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(item);
}

我如何才能做到这一点?

首先,重要的是要理解为什么你会失去这些价值观。让我们来看看

对象上的一个标量属性已修改,尚未调用SaveChanges方法。在没有更改跟踪代理的POCO实体中,调用DetectChanges方法时,已修改属性的状态更改为“已修改”。保存更改后,对象状态将更改为“未更改”

由于您的对象不是从数据库中检索的(它不在上下文中),因此所有属性都会被修改,因此将对象保存到数据库会保存所有属性

您将只希望将特定属性标记为已更改,而不是将整个对象标记为已更改:

db.Users.Attach(item);
db.Entry(item).Property(x => x.ID).IsModified = true;
db.Entry(item).Property(x => x.name).IsModified = true;
db.Entry(item).Property(x => x.att1).IsModified = true;
db.Entry(item).Property(x => x.att2).IsModified = true;
db.SaveChanges();

(如果您的
ID
是键,请不要将其标记为已修改)

首先,了解丢失这些值的原因很重要。让我们来看看

对象上的一个标量属性已修改,尚未调用SaveChanges方法。在没有更改跟踪代理的POCO实体中,调用DetectChanges方法时,已修改属性的状态更改为“已修改”。保存更改后,对象状态将更改为“未更改”

由于您的对象不是从数据库中检索的(它不在上下文中),因此所有属性都会被修改,因此将对象保存到数据库会保存所有属性

您将只希望将特定属性标记为已更改,而不是将整个对象标记为已更改:

db.Users.Attach(item);
db.Entry(item).Property(x => x.ID).IsModified = true;
db.Entry(item).Property(x => x.name).IsModified = true;
db.Entry(item).Property(x => x.att1).IsModified = true;
db.Entry(item).Property(x => x.att2).IsModified = true;
db.SaveChanges();

(如果你的
ID
是关键,不要将其标记为已修改)

虽然埃里克的回答在技术上是正确的,但我认为这有点反模式

首先,在编辑实体时,不应该只是将发布的对象直接传递到数据库以覆盖其中的任何内容

您的编辑操作应将id(或某些其他标识值)作为参数:

public ActionResult Edit(int id, Foo model)
然后,您应该从数据库中重新提取对象,并检查正在修改的对象是否正确且仍然存在:

var foo = db.Foos.Find(id);
if (foo == null)
{
    return new HttpNotFoundResult();
}
最后,将应修改的值从发布的数据映射到数据库中的实体:

foo.name = model.name;
// etc.

现在,当您保存时,只有您想要修改的值才会被修改,并且没有必要显式地篡改更改跟踪。

虽然Erik的答案在技术上是正确的,但我认为这有点反模式

首先,在编辑实体时,不应该只是将发布的对象直接传递到数据库以覆盖其中的任何内容

您的编辑操作应将id(或某些其他标识值)作为参数:

public ActionResult Edit(int id, Foo model)
然后,您应该从数据库中重新提取对象,并检查正在修改的对象是否正确且仍然存在:

var foo = db.Foos.Find(id);
if (foo == null)
{
    return new HttpNotFoundResult();
}
最后,将应修改的值从发布的数据映射到数据库中的实体:

foo.name = model.name;
// etc.

现在,当您保存时,只会修改您想要修改的值,并且不需要显式地篡改更改跟踪。

如果您想要绑定
MyClass
类型的所有属性,为什么要使用绑定?只需省略
[Bind]
属性。我只想编辑name、attr1和attr2。其余的应该保持不变。如果要绑定
MyClass
类型的所有属性,为什么要使用绑定?只需省略
[Bind]
属性。我只想编辑name、attr1和attr2。其余的应该保持不变。好的,有没有更聪明的方法可以基于绑定注释上列出的属性来实现这一点,这样就不需要逐一进行了?我认为将实体连接到表单是一种不好的做法。在这里,您最好遵循Chris的建议,通过某种映射实用程序将视图模型连接到您的实体,可以手动进行,也可以通过映射,这种映射需要按属性逐个进行。即使在Erik的回答中,您也必须手动设置每个属性的修改状态。通常,视图模型是使用
Bind
的一种更好的方法,但它仍然不能解决将值映射回实体的问题。使用自动映射库(如AutoMapper)是简化某些逻辑的一种方法。然而,该库的作者自己说,它不是也从来没有打算映射到实体,只是从。好的,有没有一种更聪明的方法,可以基于绑定注释上列出的属性来映射到实体,这样就不需要逐个映射了?我认为将实体连接到表单是一种不好的做法。在这里,您最好遵循Chris的建议,通过某种映射实用程序将视图模型连接到您的实体,可以手动进行,也可以通过映射,这种映射需要按属性逐个进行。即使在Erik的回答中,您也必须手动设置每个属性的修改状态。通常,视图模型是使用
Bind
的一种更好的方法,但它仍然不能解决将值映射回实体的问题。使用自动映射库(如AutoMapper)是简化某些逻辑的一种方法。然而,该图书馆的作者本人说,它不是,也从来不是我