首页 > 解决方案 > 使用附加或单一的实体框架更新

问题描述

我是实体框架的新手,并且看到了不同的更新方法。

public void Update (Model model)
{
   var modelInDb = context.Singe(m => m.Id == model.Id);
   modelInDb.Name = "New Name";
   context.SaveChanges();
}
public void Update (Model model)
{
   context.Customer.Attach(model);
   model.Name = "New Name";
   context.SaveChanges();
}

为什么我应该使用附加而不是单?你能解释一下区别吗?

标签: entity-framework

解决方案


在客户端和服务器之间传递实体应该被视为一种反模式,因为它会使您的系统容易受到浏览器中的人为攻击和类似的攻击。

您的 2 个示例并没有真正概述太多,因为您仅在方法中设置更新的值,而不是基于视图的输入。更常见的更新示例是:

public void Update (Model model)
{
   var modelInDb = context.Models.Singe(m => m.Id == model.Id);
   modelInDb.Name = model.Name;
   context.SaveChanges();
}

public void Update (Model model)
{
   context.Models.Attach(model);
   context.Entity(model).State = EntityState.Modified;
   context.SaveChanges();
}

在您的示例中,如果您的方法设置了修改,那么 UPDATE SQL 语句应该没问题,只需修改客户名称。但是,如果您附加模型,并将其状态设置为已修改以将新模型字段保存到数据库,它将更新所有列。

在这两个例子中,第一个比第二个更好,原因有很多。第一个示例是从上下文加载数据并仅复制您希望能够从视图中更改的数据。第二个是按原样从视图中获取模型,将其附加到上下文中,并将覆盖任何现有字段。攻击者可以发现这一点并使用该行为来更改您的视图不允许更改的数据。例如,客户订单可能包含有关订单的大量数据,包括产品关系、折扣等。用户可能在他们的视图中看不到任何这些细节,但通过传递实体图,所有这些都在网络请求数据。这不仅会向客户端发送比客户端需要的信息多得多的信息(更慢),而且还可以在到达您的服务之前在调试工具等中进行更改。附加和更新返回的实体会使您的系统受到篡改。

此外,您可能会覆盖对象中的陈旧数据。使用选项 1,您正在加载实体的“现在”副本。对传入数据和当前数据库副本之间的行版本号或上次修改日期的简单检查可以表明该行自不久前传递给客户端以来是否已更改。使用第二种方法,您可以在不经意间擦除对数据的修改而不会留下痕迹。

更好的方法是将 ViewModel 传入和传出你的视图。通过使用Select或 Automapper 来填充视图模型,您可以避免暴露更多关于您的域的内容,而不是客户端需要查看的内容。您也只接受客户端可以执行的操作所需的数据。这减少了有效负载大小并降低了篡改的漏洞。我已经看到了一些令人震惊的示例,甚至来自 Microsoft,它们在客户端和服务器之间传递实体。它看起来很实用,因为对象已经存在,但这对于资源/性能来说是浪费的,对于处理循环引用和序列化来说很麻烦,并且容易发生数据篡改和过时数据覆盖。


推荐阅读