问题描述
我对实体框架中的更改跟踪器有疑问。我在MSDN上阅读了变更跟踪器文档-它说,如果将模型插入数据库,其在上下文中的状态将为Added
,而在执行SaveChanges()
时,它将在行中插入新行。数据库,其状态为unchanged
。
那么为什么在实体框架中更新模型之前应该附加模型?
public virtual void Update(TEntity entity)
{
_dbset.Attach(entity); => ???
_context.Entry(entity).State = EntityState.Modified;
_context.SaveChanges();
}
更新:
解决方法
当实体在DbContext的范围内更改并且未加载/ w AsNoTracking
时,该DbContext的更改跟踪器将在您调用SaveChanges
时获取更改并应用更新。如您所给出的示例,在许多情况下,您可能正在处理一个DbContext不再跟踪 的实体。
举一个简单的例子:(跟踪的实体)
public void UpdateDescription(int orderId,string description)
{
using (var context = new AppDbContext())
{
var order = context.Orders.Single(x => x.OrderId == orderId);
order.Description = description;
context.SaveChanges();
}
}
在此示例中,在单个DbContext范围内加载,跟踪和更新了Order实体。相反,如果我们有这样的方法结构:
public Order GetOrderById(int orderId)
{
using (var context = new AppDbContext())
{
return context.Orders.Single(x => x.OrderId == orderId);
}
}
public void UpdateOrder(Order order)
{
using (var context = new AppDbContext())
{
context.Attach(order);
context.Entity(order).State = EntityState.Modified;
context.SaveChanges();
}
}
就像在Web应用程序中,“编辑”页面检索Order实体并将其传递到视图的情况一样。当用户更新“模型”页面上的某些字段并提交时,将传递一个单独的Update调用以传递模型。通过“ GetOrderById”调用,Order实体由DbContext的一个实例加载。发生“ UpdateOrder”调用时,该Order实际上是反序列化的POCO,不再是跟踪的实体。这就是为什么您需要将其附加到Update将要使用的DbContext实例上,并将其实体状态设置为Modified以便EF将其视为需要更新的实体。
值得注意的是,在第一个跟踪的示例中,UPDATE SQL语句实际上将类似于:
UPDATE Orders SET Description = 'newDescription' WHERE OrderId = 1
在第二个示例中,UPDATE语句将更像:
UPDATE Orders SET OrderNumber = 21,CustomerId = 12,Description = 'newDescription' /* + all fields/FKs in Order table... */ WHERE OrderId = 1
通过将实体状态附加并设置为“已修改”,即使您仅打算/期望已修改一个字段,EF也会更新该实体上的所有非PK属性。对于Web应用程序,将实体通过这种方式传递回控制器进行更新可能会使您的系统容易受到数据篡改的影响。 (更改您的UI不允许的字段/ FK)这还意味着您需要始终传递一个完整的实体,以避免数据可能意外擦除,从而增加了客户端和服务器之间来回传递的消息有效负载大小。>
,因为EntityFramework
仅更改了ChangeTracker
中存在的实体。
当您将实体附加到DbContext
时,实体框架会知道一个实体已经跟踪并可能包含更改,并在DataBase
之后在SaveChanges
中应用更改。