这篇文章将会覆盖如何新增和附加实体到上下文以及在 SaveChanges 中Entity Framework 如何处理它们。 Entity Framework 会在实体与上下文连接时追踪它们的状态,但你能在实体与上下文断开连接或者 N-Tier 场景下让 EF 知道你的实体的状态是什么。在这篇文章中展示的技术同时适用于 通过Code First 创建的模型 和 EF 设计器创建的模型 。
实体状态 和 SaveChanges
在 EntityState 枚举定义中,一个实体能够处于以下的五个状态之一。这些状态是:
1、Added(新增的):实体将会被上下文追踪但尚未持久化到数据库中
2、Unchanged(未改变的):实体在数据库中存在并且将会被上下文追踪,但它的属性值和数据库中存在的值相比是尚未改变的(一致的)
3、Modified(已修改的):实体在数据库中存在并且将会被上下文追踪,同时它的部分或全部值已经被改变
4、Deleted(被删除的):实体在数据库中存在并且将会被上下文追踪,但已经被标记为在下一次 SaveChanges 被调用时数据库中将会删除的部分
5、Detached(分离的):实体将不会被上下文追踪
SaveChanges 在实体处于不同的状态时做不同的事:
1、Unchanged 的实体不会被SaveChanges 提及到。当实体在Unchanged 状态下,更新命令将不会被发送到数据库。
2、Added 的实体在SaveChanges 结束时会被插入到数据中,并且在上下文中的状态会变成 Unchanged 。
3、Modified 的实体在SaveChanges 结束时会被更新到数据库中,并且在上下文中的状态会变成 Unchanged 。
4、Deleted 的实体在SaveChanges 结束时会从数据库中删除,然后从当前上下文中分离,即 Detached。
下面的例子展示了实体的状态和实体图能够被改变的方式
添加一个实体到上下文中
一个实体能在数据集中通过调用 Add 方法被添加到上下文中。这将会使实体处于Added 状态,意味着它将在下次调用SaveChanges 方法时被插入到数据库中,例如:
using (var context = new BloggingContext())
{
var blog = new Blog { Name = "ADO.NET Blog" };
context.Blogs.Add(blog);
context.SaveChanges();
}
另一种添加一个实体到上下文中的方式是改变它的状态为 Added 。例如:
using (var context = new BloggingContext())
{
var blog = new Blog { Name = "ADO.NET Blog" };
context.Entry(blog).State = EntityState.Added;
context.SaveChanges();
}
最后,你也可以通过将一个实体挂勾到一个已被上下文追踪的实体来添加它到上下文中。这可以通过将新实体添加到另一个实体的导航属性集合中或着通过设置另一个实体的导航属性引用指向新实体。例如:
using (var context = new BloggingContext())
{
// 通过从已被追踪的Blog中设置引用来新增一个新 User
var blog = context.Blogs.Find(1);
blog.Owner=new User{UserName="johndoe1987"});
//通过添加到一个已被追踪的Blog中的集合中来新增一个新 User
var blog = context.Blogs.Find(2);
blog.Posts.Add(new Post{ Name ="How to Add Entities" });
context.SaveChanges();
}
注意以上所有的例子中,如果一个被新增的实体已关联其他未被上下文追踪的实体,这些新实体将会被新增到上下文中,并且会在下次调用SaveChanges 时插入到数据库中。
附加一个已存在的实体到上下文中
如果你有一个已知的已在数据库中存在但当前未被上下文追踪的实体,你能通过在数据集上使用 Attach 方法来告知上下文去追踪这个实体。这个实体将处于 Unchanged 状态。例如:
var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Blogs.Attach(existingBlog);
// Do some more work…
context.SaveChanges();
}
注意如果调用SaveChanges 时没有对附加的实体进行其他的操作,那么对数据库将不会有任何的改变。这是因为这个实体处于Unchanged状态下。
另一个附加已存在的实体到上下文中的方式是改变它的状态为Unchanged。例如:
var existingBlog = new Blog { BlogId = 1,Name="ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Entry(existingBlog).State = EntityState.Unchanged;
//Do some more work…
context.SaveChanges();
}
注意在以上的例子中,如果被附加的实体已关联其他尚未被追踪的实体,那么这些新实体也会被附加到以Unchanged的状态附加到上下文中。
附加一个已存在但已被修改的实体到上下文中
如果你有一个已经在数据库中存在并将会被改变的实体,你可以通过设置它的状态为Modified 来告知上下文去附加这个实体。例如:
var existingBlog = new Blog {BlogId=1,Name="ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Entry(existingBlog).State = EntityState.Modified;
//Do some more work…
context.SaveChanges();
}
当你修改实体的状态为 Modified 时,该实体的所有属性将会被标识为已修改并所有的属性值将会在SaveChanges被调用时发送到数据库。
注意如果一个已被附加的实体已关联其他尚未被追踪的实体,那么这些实体将会以Unchanged的状体附加到上下文中——它们不会自动被修改。如果你有多个实体需要被标识为Modified ,你应该单独设置每一个实体的状态。
修改已被追踪的实体的状态
你可以在一个已被追踪的实体进入上下文时通过设置它的状态来直接修改状态
var existingBlog = new Blog { BlogId = 1,Name = "ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Blogs.Attach(existingBlog);
context.Entry(existingBlog).State = EntityState.Unchanged;
//Do some more work…
context.SaveChanges();
}
注意 为一个已被追踪的实体调用 Add 或者 Attach 方法通常用来修改实体的状态。例如,为一个已处于Added 状态下的实体调用 Attach 方法会修改它的状态为 Unchanged。
新增或者更新模式
一些应用程序中的一个常见的模式是根据主键的值来新增一个实体(导致数据库插入)或者附加一个已存在的实体并标识其为已修改(导致数据库更新)。例如,在使用数据库自增整型作为主键时,通过识别主键是否为0来判断一个实体是新实体还是已存在的是很常见的(实体的主键为0时视为新实体,主键非0时视为已存在)。这种模式能通过基于检查主键值来设置实体状态来实现。例如:
public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
注意当你修改实体的状态为 Modified 时,该实体的所有属性将会被标识为已修改并所有的属性值将会在SaveChanges被调用时发送到数据库。
来源:
https://msdn.microsoft.com/en-us/library/jj592676(v=vs.113).aspx