随着 .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 应用程序的启动速度显著提升,预编译的查询直接嵌入到应用程序二进制文件中。
编译查询是 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);
}
| 查询类型 | 常规查询 | 编译查询 | 性能提升 | |----------|----------|----------|----------| | 简单过滤 | 152.3 毫秒 | 98.7 毫秒 | 快 35% | | 复杂连接 | 284.1 毫秒 | 189.6 毫秒 | 快 33% | | 投影查询 | 97.5 毫秒 | 71.2 毫秒 | 快 27% |
DbContext 池化常常被忽视,但它能带来显著的性能提升,尤其在高吞吐量场景中。EF Core 9 增强了池化机制,请求吞吐量提升了 2 倍。
// 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 实例数 | 64,637 | 329 | 减少 99.95% | | 每秒请求数 | 6,395 | 15,714 | 增加 146% | | 内存分配 | 高 | 低 | 减少 60% |
变更跟踪是 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();
EF 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();
| 操作类型 | 传统方法 | 批量方法 | 性能提升 | |----------|----------|----------|----------| | 更新 10K 条记录 | 6.2 秒 | 0.13 秒 | 快 47 倍 | | 删除 10K 条记录 | 8.1 秒 | 0.26 秒 | 快 31 倍 | | 内存使用 | 高 | 低 | 减少 80% |
高效的分页对于处理大型数据集的应用程序至关重要。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
};
}
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;
}
}
.NET 9 中的原生 AOT 编译为 EF Core 应用程序提供了显著的启动性能改进。
<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>
[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);
}
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)));
EF Core 9 显著增强了 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);
});
}
// 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();