随着 .NET 9 的发布,Entity Framework Core 经历了显著的性能改进,为开发人员提供了强大的新工具和优化手段,可大幅提升查询性能。凭借性能增强、AOT 编译支持以及复杂的缓存机制,EF Core 9 的查询性能较以往版本提升了高达 30%。本指南将深入探讨在 .NET 9 中充分挖掘 EF Core 查询性能的高级技术。
EF Core 9 带来了多项突破性的性能增强,从根本上改变了我们进行查询优化的方式:
• 增强的查询编译:查询编译管道经过全面优化,对于复杂的 LINQ 操作,翻译开销减少了高达 30%。这种改进在涉及多个连接和复杂过滤逻辑的场景中尤为明显。• 改进的批处理操作:EF Core 9 引入了复杂的批处理算法,可智能地对 SQL 命令进行分组,减少与数据库的往返次数。对于 SQL Server,最佳批处理大小已优化为每批 42 条语句,性能分析表明超过此阈值后收益会递减。• 原生 AOT 支持:.NET 9 的原生预编译(AOT)功能使 EF Core 应用程序的启动速度显著提升,预编译的查询直接嵌入到应用程序二进制文件中。1. 编译查询:性能提升的关键利器编译查询是 EF Core 9 中最具影响力的优化之一。通过预编译 LINQ 表达式,对于频繁执行的查询,您可以实现 10-30% 的性能提升。
基本编译查询实现public class ProductRepository{ // 静态编译查询,实现最大程度的复用 private static readonly Func<AppDbContext, int, Product?> _getProductById = EF.CompileQuery((AppDbContext ctx, int id) => ctx.Products .Include(p => p.Category) .FirstOrDefault(p => p.Id == id));private readonly AppDbContext _context; public ProductRepository(AppDbContext context) => _context = context; public Product? GetProductById(int id) => _getProductById(_context, id);}异步编译查询:提升可扩展性public class OrderService{ private static readonly Func<AppDbContext, DateTime, CancellationToken, Task<List<Order>>> _getRecentOrdersAsync = EF.CompileAsyncQuery( (AppDbContext ctx, DateTime fromDate, CancellationToken ct) => ctx.Orders .Where(o => o.CreatedDate >= fromDate) .Include(o => o.OrderItems) .OrderByDescending(o => o.CreatedDate));public async Task<List<Order>> GetRecentOrdersAsync(DateTime fromDate, CancellationToken ct = default) => await _getRecentOrdersAsync(_context, fromDate, ct);}性能基准测试:编译查询 vs 常规查询查询类型常规查询编译查询性能提升简单过滤152.3 毫秒98.7 毫秒快 35%复杂连接284.1 毫秒189.6 毫秒快 33%投影查询97.5 毫秒71.2 毫秒快 27%
2. DbContext 池化:减少对象创建开销DbContext 池化常常被忽视,但它能带来显著的性能提升,尤其在高吞吐量场景中。EF Core 9 增强了池化机制,请求吞吐量提升了 2 倍。
配置 DbContext 池化// Program.cs - .NET 9 风格var builder = WebApplication.CreateBuilder(args);builder.Services.AddDbContextPool<AppDbContext>(options =>{ options.UseSqlServer(connectionString); options.EnableSensitiveDataLogging(false); // 生产环境中禁用 options.EnableServiceProviderCaching();}, poolSize: 128); // 大多数场景的最佳池大小var app = builder.Build();DbContext 池化的性能影响指标无池化有池化改进效果创建的 DbContext 实例数64,637329减少 99.95%每秒请求数6,39515,714增加 146%内存分配高低减少 60%
3. 高级查询优化技术用于只读操作的 AsNoTracking变更跟踪是 EF Core 中最显著的性能开销来源之一。对于只读查询,禁用跟踪可立即带来性能收益。
// ❌ 启用跟踪(默认)- 速度较慢var products = await context.Products .Include(p => p.Category) .ToListAsync();// ✅ 无跟踪 - 显著更快var products = await context.Products .AsNoTracking() .Include(p => p.Category) .ToListAsync();// ✅ 只读上下文的全局无跟踪配置builder.Services.AddDbContext<ReadOnlyDbContext>(options => options.UseSqlServer(connectionString) .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));拆分查询:避免笛卡尔积爆炸EF Core 9 增强了拆分查询功能,可防止在加载多个相关集合时出现笛卡尔积爆炸。
// ❌ 单查询 - 可能导致笛卡尔积爆炸var blogs = await context.Blogs .Include(b => b.Posts) .Include(b => b.Tags) .ToListAsync();// ✅ 拆分查询 - 多个优化的查询var blogs = await context.Blogs .Include(b => b.Posts) .Include(b => b.Tags) .AsSplitQuery() .ToListAsync();// ✅ 全局拆分查询配置protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ optionsBuilder.UseSqlServer(connectionString, o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));}高效投影:只选择需要的字段投影通过仅检索所需字段,大幅减少数据传输和内存使用。
// ❌ 加载整个实体var customers = await context.Customers .Include(c => c.Orders) .ToListAsync();// ✅ 优化的投影var customerSummaries = await context.Customers .Select(c => new CustomerSummaryDto { Id = c.Id, Name = c.Name, Email = c.Email, OrderCount = c.Orders.Count(), TotalSpent = c.Orders.Sum(o => o.Total) }) .ToListAsync();4. 批量操作:ExecuteUpdate 和 ExecuteDeleteEF Core 9 显著改进了批量操作,与传统方法相比,更新速度快 6 倍,删除速度快 3 倍。
优化的批量更新// ❌ 传统方法 - 对于大型数据集速度慢var products = await context.Products .Where(p => p.CategoryId == categoryId) .ToListAsync();foreach (var product in products){ product.Price *= 1.1m; // 价格提高 10%}await context.SaveChangesAsync();// ✅ 批量更新 - 速度显著提升await context.Products .Where(p => p.CategoryId == categoryId) .ExecuteUpdateAsync(p => p.SetProperty(x => x.Price, x => x.Price * 1.1m));高效的批量删除// ❌ 传统删除 - 多次往返数据库var oldOrders = await context.Orders .Where(o => o.CreatedDate < DateTime.Now.AddYears(-2)) .ToListAsync();context.Orders.RemoveRange(oldOrders);await context.SaveChangesAsync();// ✅ 批量删除 - 单个高效操作await context.Orders .Where(o => o.CreatedDate < DateTime.Now.AddYears(-2)) .ExecuteDeleteAsync();性能对比:批量操作 vs 传统操作操作类型传统方法批量方法性能提升更新 10K 条记录6.2 秒0.13 秒快 47 倍删除 10K 条记录8.1 秒0.26 秒快 31 倍内存使用高低减少 80%
5. 分页和大型数据集优化高效的分页对于处理大型数据集的应用程序至关重要。EF Core 9 提供了增强的分页功能。
基于游标的分页(推荐)public async Task<PaginatedResult<ProductDto>> GetProductsAsync( int? lastProductId = null, int pageSize = 50){ var query = context.Products.AsNoTracking(); if (lastProductId.HasValue) { query = query.Where(p => p.Id > lastProductId.Value); } var products = await query .OrderBy(p => p.Id) .Take(pageSize + 1) // 多取一条以检查是否有更多数据 .Select(p => new ProductDto { Id = p.Id, Name = p.Name, Price = p.Price }) .ToListAsync(); var hasMore = products.Count > pageSize; if (hasMore) { products.RemoveAt(pageSize); } return new PaginatedResult<ProductDto> { Items = products, HasMore = hasMore, LastId = products.LastOrDefault()?.Id };}带优化的基于偏移量的分页// ✅ 优化的基于偏移量的分页public async Task<PagedResult<T>> GetPagedAsync<T>( IQueryable<T> query, int pageNumber, int pageSize){ var totalCount = await query.CountAsync(); var items = await query .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToListAsync(); return new PagedResult<T> { Items = items, TotalCount = totalCount, PageNumber = pageNumber, PageSize = pageSize };}6. 连接和事务优化增强的连接池builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString, sqlOptions => { sqlOptions.CommandTimeout(30); sqlOptions.EnableRetryOnFailure( maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(10), errorNumbersToAdd: null); }));// 连接字符串优化var connectionString = "Server=server;Database=db;User Id=user;Password=pass;" + "Min Pool Size=5;Max Pool Size=100;Connection Timeout=30;" + "Pooling=true;MultipleActiveResultSets=true";优化的事务使用public async Task<Result> ProcessOrderAsync(OrderRequest request){ using var transaction = await context.Database.BeginTransactionAsync(); try { // 批量处理多个操作 var order = new Order(request); context.Orders.Add(order); // 在事务中使用批量操作 await context.Products .Where(p => request.ProductIds.Contains(p.Id)) .ExecuteUpdateAsync(p => p.SetProperty(x => x.Stock, x => x.Stock - 1)); await context.SaveChangesAsync(); await transaction.CommitAsync(); return Result.Success(); } catch { await transaction.RollbackAsync(); throw; }}7. 利用 .NET 9 原生 AOT 功能.NET 9 中的原生 AOT 编译为 EF Core 应用程序提供了显著的启动性能改进。
为 EF Core 配置 AOT<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net9.0</TargetFramework> <PublishAot>true</PublishAot> <InvariantGlobalization>true</InvariantGlobalization> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0" /> </ItemGroup></Project>兼容 AOT 的仓储模式[JsonSerializable(typeof(List<ProductDto>))][JsonSerializable(typeof(ProductDto))]internal partial class AppJsonSerializerContext : JsonSerializerContext{}public class ProductService{ private static readonly Func<AppDbContext, Task<List<ProductDto>>> _getAllProductsCompiled = EF.CompileAsyncQuery((AppDbContext ctx) => ctx.Products .AsNoTracking() .Select(p => new ProductDto { Id = p.Id, Name = p.Name, Price = p.Price })); public async Task<List<ProductDto>> GetAllProductsAsync() => await _getAllProductsCompiled(_context);}8. 监控和诊断性能拦截器public class PerformanceInterceptor : DbCommandInterceptor{ private readonly ILogger<PerformanceInterceptor> _logger; private readonly int _slowQueryThresholdMs;public PerformanceInterceptor(ILogger<PerformanceInterceptor> logger, int slowQueryThresholdMs = 1000) { _logger = logger; _slowQueryThresholdMs = slowQueryThresholdMs; } public override async ValueTask<DbDataReader> ReaderExecutedAsync( DbCommand command, CommandExecutedEventData eventData, DbDataReader result, CancellationToken cancellationToken = default) { if (eventData.Duration.TotalMilliseconds > _slowQueryThresholdMs) { _logger.LogWarning("检测到慢查询:{Duration}毫秒 - {CommandText}", eventData.Duration.TotalMilliseconds, command.CommandText); } return await base.ReaderExecutedAsync(command, eventData, result, cancellationToken); }}// 注册builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString) .AddInterceptors(new PerformanceInterceptor(logger)));9. EF Core 9 中的 JSON 列优化EF Core 9 显著增强了 JSON 列支持,为复杂数据结构提供了高效的存储和查询能力。
优化的 JSON 配置public class Product{ public int Id { get; set; } public string Name { get; set; } public ProductMetadata Metadata { get; set; } // 存储为 JSON}public class ProductMetadata{ public List<string> Tags { get; set; } public Dictionary<string, object> Attributes { get; set; } public Address ShippingAddress { get; set; }}// 配置protected override void OnModelCreating(ModelBuilder modelBuilder){ modelBuilder.Entity<Product>() .OwnsOne(p => p.Metadata, metadata => { metadata.ToJson(); // 存储为 JSON 列 metadata.OwnsOne(m => m.ShippingAddress); });}高效的 JSON 查询// EF Core 9 中的优化 JSON 查询var products = await context.Products .AsNoTracking() .Where(p => p.Metadata.Tags.Contains("electronics")) .Where(p => p.Metadata.Attributes["warranty"] == "2years") .Select(p => new ProductSummaryDto { Id = p.Id, Name = p.Name, Tags = p.Metadata.Tags, HasWarranty = p.Metadata.Attributes.ContainsKey("warranty") }) .ToListAsync();
京公网安备11000000000001号
京ICP备11000001号
还没有评论,来说两句吧...