保存数据¶
每个上下文实例都有一个 ChangeTracker,它负责跟踪需要写入数据库的更改。 更改实体类的实例时,这些更改会记录在 ChangeTracker 中,然后在调用 SaveChanges 时被写入数据库。 此数据库提供程序负责将更改转换为特定于数据库的操作(例如,关系数据库的 INSERT、UPDATE 和 DELETE 命令)。
基本保存¶
添加数据
using (var context = new BloggingContext())
{
var blog = new Blog { Url = "http://example.com" };
context.Blogs.Add(blog);
context.SaveChanges();
}
更新数据
using (var context = new BloggingContext())
{
var blog = context.Blogs.First();
blog.Url = "http://example.com/blog";
context.SaveChanges();
blog.Url = "http://example.com/blog1";
context.Blogs.Attach(blog);
context.SaveChanges();
blog.Url = "http://example.com/blog2";
context.Blogs.Update(blog);
context.SaveChanges();
}
删除数据
using (var context = new BloggingContext())
{
var blog = context.Blogs.First();
context.Blogs.Remove(blog);
context.SaveChanges();
}
保存相关数据¶
添加新实体的关系图
using (var context = new BloggingContext())
{
var blog = new Blog
{
Url = "http://blogs.msdn.com/dotnet",
Posts = new List<Post>
{
new Post { Title = "Intro to C#" },
new Post { Title = "Intro to VB.NET" },
new Post { Title = "Intro to F#" }
}
};
context.Blogs.Add(blog);
context.SaveChanges();
}
添加相关实体
using (var context = new BloggingContext())
{
var blog = context.Blogs.Include(b => b.Posts).First();
var post = new Post { Title = "Intro to EF Core" };
blog.Posts.Add(post);
context.SaveChanges();
}
更改关系
using (var context = new BloggingContext())
{
var blog = new Blog { Url = "http://blogs.msdn.com/visualstudio" };
var post = context.Posts.First();
post.Blog = blog;
context.SaveChanges();
}
删除关系
可以通过将引用导航设置为 null 或从集合导航中删除相关实体来删除关系。
根据关系中配置的级联删除行为,删除关系可能会对依赖实体产生副作用。
默认情况下,对于必选关系,将配置级联删除行为,并将从数据库中删除子实体/依赖实体。
对于可选关系,默认情况下不会配置级联删除,但会将外键属性设置为 null。
using (var context = new BloggingContext())
{
var blog = context.Blogs.Include(b => b.Posts).First();
var post = blog.Posts.First();
blog.Posts.Remove(post);
context.SaveChanges();
}
异步保存¶
当所做的更改被写入数据库时,异步保存可避免阻塞线程。 这有助于避免冻结富客户端应用程序的 UI。 异步操作还可以增加 Web 应用程序的吞吐量,可以在数据库操作完成时释放线程去处理其他请求。
级联删除¶
级联删除通常在数据库术语中用来描述一种允许在删除某行时自动触发删除相关行的特性。
EF Core
删除行为还介绍了一个密切相关的概念,即子实体与父实体的关系已断开时自动删除该子实体,这通常称为“删除孤立项”。
Entity Framework Core 提供了 DbContext.SaveChangesAsync() 作为
DbContext.SaveChanges() 的异步替代方法。
public static async Task AddBlogAsync(string url)
{
using (var context = new BloggingContext())
{
var blog = new Blog { Url = url };
context.Blogs.Add(blog);
await context.SaveChangesAsync();
}
}
处理并发冲突¶
数据库并发指多个进程或用户同时访问或更改数据库中的相同数据的情况。
并发控制_指的是用于在发生并发更改时确保数据一致性的特定机制。
EF Core
实现了乐观并发控制,这意味着它将允许多个进程或用户独立进行更改,而不会产生同步或锁定的开销。
在理想情况下,这些更改将不会相互干扰,因此都能够成功。
在最坏的情况下,两个或更多进程将尝试进行冲突更改,而其中只有一个进程会成功。
事务¶
事务允许以原子方式处理多个数据库操作。 如果已提交事务,则所有操作都会成功应用到数据库。 如果已回滚事务,则所有操作都不会应用到数据库。
默认事务行为¶
默认情况下,如果数据库提供程序支持事务,则会在单次调用 SaveChanges()
时将所有更改都将应用到事务中。
如果其中有任何更改失败,则会回滚事务且所有更改都不会应用到数据库。
这意味着,SaveChanges()
可保证要么完全成功,要么在出现错误时不修改数据库。
对于大多数应用程序,此默认行为已足够。
除非应用程序确有需求,否则不应手动控制事务。
控制事务¶
using (var context = new BloggingContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
context.SaveChanges();
var blogs = context.Blogs
.OrderBy(b => b.Url)
.ToList();
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
}
}
共享连接和事务¶
现在可以创建共享同一连接的多个上下文实例。 然后使用 DbContext.Database.UseTransaction(DbTransaction) API 在同一事务中登记两个上下文。
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(new SqlConnection(connectionString))
.Options;
using (var context1 = new BloggingContext(options))
{
using (var transaction = context1.Database.BeginTransaction())
{
try
{
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context1.SaveChanges();
using (var context2 = new BloggingContext(options))
{
context2.Database.UseTransaction(transaction.GetDbTransaction());
var blogs = context2.Blogs
.OrderBy(b => b.Url)
.ToList();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
}
}
使用外部 DbTransactions¶
如果使用多个数据访问技术来访问关系数据库,则可能希望在这些不同技术所执行的操作之间共享事务。
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();
// Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
using (var context = new BloggingContext(options))
{
context.Database.UseTransaction(transaction);
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (System.Exception)
{
// TODO: Handle failure
}
}
}
System.Transactions¶
提供程序暂不支持