客户端异步 LINQ 运算符
var groupedHighlyRatedBlogs = await context.Blogs .AsQueryable() .Where(b => b.Rating > 3) // server-evaluated .AsAsyncEnumerable() .GroupBy(b => b.Rating) // client-evaluated .ToListAsync();
Logging
EF Core 会自动与 ASP.NET Core 的日志记录机制集成 AddDbContext
AddDbContextPool
DbContextPool
services.AddDbContextPool<BloggingContext>(
options => options.UseSqlServer(connectionString));
上下文池的工作方式是跨请求重复使用同一上下文实例。 这意味着,它可以有效地根据实例本身注册为 单一 实例,以便能够持久保存。
对于需要 范围内 的服务或需要更改配置的情况,请不要使用 pooling。 除高度优化的方案外,池的性能提升通常可以忽略不计。
连接复原
连接复原会自动重试失败的数据库命令。 此功能可用于任何数据库,方法是提供 "执行策略",它封装了检测失败和重试命令所需的逻辑。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlServer( @"Server=(localdb)mssqllocaldb;Database=EFMiscellanous.ConnectionResiliency;Trusted_Connection=True;ConnectRetryCount=0", options => options.EnableRetryOnFailure()); } or public void ConfigureServices(IServiceCollection services) { services.AddDbContext<PicnicContext>( options => options.UseSqlServer( "<connection string>", providerOptions => providerOptions.EnableRetryOnFailure())); }
如果发生暂时性故障,每个查询和每个调用 SaveChanges()
都将作为一个单元重试。但是,如果您的代码使用 BeginTransaction()
您定义自己的一组操作,这些操作需要被视为一个单元,则需要播放该事务中的所有内容时,都将发生故障。
解决方案是使用代表需要执行的所有内容的委托手动调用执行策略。
using (var db = new BloggingContext()) { var strategy = db.Database.CreateExecutionStrategy(); strategy.Execute(() => { using (var context = new BloggingContext()) { using (var transaction = context.Database.BeginTransaction()) { 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(); transaction.Commit(); } } }); }
事务提交失败
对于大多数更改数据库状态的操作,可以添加代码来检查它是否成功。 EF 提供扩展方法来简化此过程 IExecutionStrategy.ExecuteInTransaction
。
此方法会启动并提交事务,还会接受参数中的函数,该 verifySucceeded
参数在事务提交期间发生暂时性错误时被调用。
using (var db = new BloggingContext()) { var strategy = db.Database.CreateExecutionStrategy(); var blogToAdd = new Blog {Url = "http://blogs.msdn.com/dotnet"}; db.Blogs.Add(blogToAdd); strategy.ExecuteInTransaction(db, operation: context => { context.SaveChanges(acceptAllChangesOnSuccess: false); }, verifySucceeded: context => context.Blogs.AsNoTracking().Any(b => b.BlogId == blogToAdd.BlogId)); db.ChangeTracker.AcceptAllChanges(); }
在调用时,将 SaveChanges
acceptAllChangesOnSuccess
设置为 false
,以避免在成功时将实体的状态更改 为 Unchanged
。 如果提交失败并且事务已回滚,则允许重试相同的操作。
如果需要使用存储生成的密钥,或者需要一种常规方法来处理不依赖于执行的操作的提交失败,则可以为其分配一个在提交失败时检查的 ID。
- 向数据库添加一个用于跟踪事务状态的表Transactions。
- 在每个事务开始时向表中插入一行。
- 如果在提交期间连接失败,请检查数据库中是否存在相应的行。
- 如果提交成功,则删除相应的行以避免表增长。
using (var db = new BloggingContext()) { var strategy = db.Database.CreateExecutionStrategy(); db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" }); var transaction = new TransactionRow {Id = Guid.NewGuid()}; db.Transactions.Add(transaction); strategy.ExecuteInTransaction(db, operation: context => { context.SaveChanges(acceptAllChangesOnSuccess: false); }, verifySucceeded: context => context.Transactions.AsNoTracking().Any(t => t.Id == transaction.Id)); db.ChangeTracker.AcceptAllChanges(); db.Transactions.Remove(transaction); db.SaveChanges(); }
避免 DbContext 线程问题
不支持在同一实例上运行多个并行操作 DbContext
。 这包括异步查询的并行执行以及从多个线程进行的任何显式并发使用。 因此,应 await
立即异步调用,或 DbContext
对并行执行的操作使用单独的实例。
AddDbContext
DbContext
默认情况下,扩展方法使用范围生存期注册类型。任何并行执行多个线程的代码都应确保 DbContext
不会同时访问实例。