EF Core规范模式实战:封装查询逻辑提升代码可维护性 这次我们来看一个在 EF Core 项目中提升代码可维护性的实用模式规范模式。如果你在项目中遇到过查询逻辑散落在各个服务层、重复的 Where 条件难以复用、或者单元测试时难以模拟查询逻辑的问题那么这个模式值得你花十分钟了解一下。它不是一个新的框架而是一种设计思想核心是使用“规范”对象来封装查询条件让查询逻辑变得清晰、可组合且易于测试。简单来说规范模式就是将你的查询条件比如Where(x x.IsActive x.CreatedDate DateTime.Now.AddDays(-7))封装成一个独立的、可复用的类。这样做的好处是你可以像搭积木一样组合不同的查询条件避免在业务逻辑中直接编写复杂的 LINQ 表达式从而让代码更干净也更容易应对变化。本文将带你从零开始理解规范模式的核心概念并手把手演示如何在 EF Core 项目中实现它。我们会重点关注它的实际应用如何定义规范、如何组合规范、如何在仓储层或查询服务中使用以及它如何简化你的单元测试。如果你关心代码结构、团队协作和长期维护成本这篇文章可以直接收藏。1. 核心能力速览在深入代码之前我们先通过一个表格快速了解规范模式能为你带来什么以及它的典型应用场景。能力项说明核心目标封装和复用查询业务逻辑实现关注点分离。技术实现基于 C# 的表达式树和 EF Core 的IQueryableT接口。主要功能1.条件封装将Where条件封装为独立类。2.逻辑组合支持与And、或Or、非Not组合。3.排序与分页可扩展以包含OrderBy、Skip、Take逻辑。4.查询复用同一规范可在不同查询场景中复用。推荐使用场景1. 复杂动态查询如高级搜索过滤器。2. 查询逻辑需要在多处复用。3. 需要为查询逻辑编写单元测试。4. 希望业务层与数据访问层解耦。不适用场景1. 极其简单的、一次性的查询。2. 对性能有极致要求需要手动优化 SQL 的场景规范模式生成的 SQL 与手写 LINQ 一致。启动/集成方式非独立服务需作为类库集成到现有 EF Core 项目中。硬件/环境门槛无特殊要求与运行 EF Core 应用的环境一致。2. 适用场景与使用边界规范模式并非银弹理解其适用边界能帮助你更好地决策。它非常适合以下场景复杂搜索/过滤例如电商后台的商品搜索需要根据价格区间、分类、品牌、库存状态、关键词等多个动态条件组合查询。将这些条件封装成不同的规范组合起来非常清晰。权限数据过滤在多租户系统或数据权限复杂的系统中查询数据时往往需要附加“当前用户所属部门”、“数据可见范围”等条件。将这些权限条件封装为规范可以确保所有查询自动应用避免遗漏。领域驱动设计在 DDD 中规范模式是领域层表达查询意图的一种方式可以将领域知识如“获取所有未过期的活跃订单”封装起来供应用层调用。单元测试你可以轻松地 Mock 一个规范对象来测试服务层逻辑而无需真正连接数据库或构建复杂的IQueryable测试数据。它的使用边界和注意事项性能规范模式最终生成的是标准的 LINQ 表达式树由 EF Core 转换为 SQL其性能与直接手写 LINQ 查询相当。但过度复杂的规范组合可能生成低效的 SQL仍需通过 EF Core 的日志如“慢查询日志”进行监控和优化。简单查询对于DbContext.Users.FindAsync(id)或简单的Where(x x.Id id)直接编写 LINQ 更简洁引入规范模式反而增加了复杂度。过度设计风险在小型项目或查询逻辑极其稳定的模块中引入规范模式可能带来不必要的抽象层。评估其带来的维护收益是否大于实现成本。理解成本团队需要理解表达式树和IQueryable的延迟执行特性否则可能在无意中导致客户端评估Client-side evaluation或 N1 查询问题。3. 环境准备与前置条件在开始编码前请确保你的开发环境满足以下要求。规范模式本身不依赖特定外部库但需要 EF Core 作为基础。开发环境操作系统Windows 10/11, macOS, 或 Linux 发行版。IDE/编辑器Visual Studio 2022、Visual Studio Code 或 Rider。.NET SDK需要 .NET 6.0、.NET 8.0 或更高版本。本文示例基于 .NET 8。项目与依赖一个已有的或新建的 ASP.NET Core Web API 或类库项目。通过 NuGet 安装Microsoft.EntityFrameworkCore和对应的数据库提供程序如Microsoft.EntityFrameworkCore.SqlServer。一个已定义的 DbContext 和实体类。例如我们用一个简单的Product产品实体来演示。// 示例实体 public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public int StockQuantity { get; set; } public bool IsActive { get; set; } public DateTime CreatedDate { get; set; } public int CategoryId { get; set; } public Category Category { get; set; } }知识准备熟悉 C# 语言特性特别是 Lambda 表达式和泛型。理解 EF Core 的基本用法和IQueryableT的延迟查询机制。对表达式树有基本概念知道ExpressionFuncT, bool是什么即可。4. 核心概念与基础实现让我们从最核心的接口开始一步步构建规范模式的基础设施。4.1 定义规范接口规范模式的核心是一个接口它定义了“某个对象是否满足特定条件”。在查询上下文中这个条件就是针对实体T的布尔表达式。// Specification.cs using System.Linq.Expressions; namespace YourProject.Specifications { public interface ISpecificationT { // 核心获取封装了查询条件的表达式树 ExpressionFuncT, bool Criteria { get; } // 可扩展点包含的导航属性用于 Include ListExpressionFuncT, object Includes { get; } // 可扩展点排序表达式 ExpressionFuncT, object OrderBy { get; } ExpressionFuncT, object OrderByDescending { get; } // 可扩展点分页 int Take { get; } int Skip { get; } bool IsPagingEnabled { get; } } }这个ISpecificationT接口是规范模式的基石。Criteria属性是最重要的它直接对应Where子句。其他属性如Includes、OrderBy等是为了让规范能描述更完整的查询。4.2 实现基础规范抽象类接下来我们实现一个基础的抽象类它提供了Includes、OrderBy等属性的存储和设置方法并实现了Criteria的核心逻辑。// BaseSpecification.cs using System.Linq.Expressions; namespace YourProject.Specifications { public abstract class BaseSpecificationT : ISpecificationT { // 核心查询条件 public ExpressionFuncT, bool Criteria { get; private set; } // 包含的导航属性列表 public ListExpressionFuncT, object Includes { get; } new ListExpressionFuncT, object(); // 排序 public ExpressionFuncT, object OrderBy { get; private set; } public ExpressionFuncT, object OrderByDescending { get; private set; } // 分页 public int Take { get; private set; } public int Skip { get; private set; } public bool IsPagingEnabled { get; private set; } // 构造函数用于创建带有查询条件的规范 protected BaseSpecification(ExpressionFuncT, bool criteria) { Criteria criteria; } // 构造函数用于创建无初始条件的规范后续可通过方法添加 protected BaseSpecification() { } // 保护方法供派生类设置条件用于无参构造时后续构建 protected void SetCriteria(ExpressionFuncT, bool criteria) { Criteria criteria; } // 添加 Include 导航属性 protected void AddInclude(ExpressionFuncT, object includeExpression) { Includes.Add(includeExpression); } // 设置升序排序 protected void ApplyOrderBy(ExpressionFuncT, object orderByExpression) { OrderBy orderByExpression; } // 设置降序排序 protected void ApplyOrderByDescending(ExpressionFuncT, object orderByDescendingExpression) { OrderByDescending orderByDescendingExpression; } // 启用分页 protected void ApplyPaging(int skip, int take) { Skip skip; Take take; IsPagingEnabled true; } } }4.3 创建第一个具体规范现在我们可以基于BaseSpecification创建具体的业务规范了。例如一个“获取所有活跃产品”的规范。// ActiveProductSpecification.cs namespace YourProject.Specifications { public class ActiveProductSpecification : BaseSpecificationProduct { public ActiveProductSpecification() : base(p p.IsActive true) // 在基类构造函数中设置条件 { // 可以同时添加 Include例如包含分类信息 AddInclude(p p.Category); // 可以设置默认排序 ApplyOrderBy(p p.CreatedDate); } } }看这个ActiveProductSpecification类非常简洁。它清晰地表达了业务意图“一个活跃产品的规范”。所有的查询细节都被封装在里面。5. 构建规范评估器Specification Evaluator有了规范对象我们还需要一个工具能将规范应用到 EF Core 的DbSetT或IQueryableT上生成最终的查询。这就是规范评估器。// SpecificationEvaluator.cs using Microsoft.EntityFrameworkCore; namespace YourProject.Specifications { public class SpecificationEvaluatorT where T : class { public static IQueryableT GetQuery(IQueryableT inputQuery, ISpecificationT specification) { var query inputQuery; // 1. 应用 Where 条件 if (specification.Criteria ! null) { query query.Where(specification.Criteria); } // 2. 应用 Includes (贪婪加载) query specification.Includes .Aggregate(query, (current, include) current.Include(include)); // 3. 应用排序 if (specification.OrderBy ! null) { query query.OrderBy(specification.OrderBy); } else if (specification.OrderByDescending ! null) { query query.OrderByDescending(specification.OrderByDescending); } // 4. 应用分页 if (specification.IsPagingEnabled) { query query.Skip(specification.Skip).Take(specification.Take); } return query; } } }这个SpecificationEvaluator是一个静态工具类。它接收一个初始的IQueryableT和一个规范对象然后按顺序应用条件、包含、排序和分页返回一个新的IQueryableT。关键点它返回的仍然是IQueryable这意味着查询还没有执行EF Core 会在最终迭代如.ToListAsync()时将所有操作合并成一条高效的 SQL 语句。6. 在仓储层或服务层中使用规范如何将规范用起来通常我们在仓储模式中集成或者直接在服务层调用。这里展示一个集成到泛型仓储中的方法。6.1 扩展 IRepository 接口和实现首先在现有的仓储接口中添加基于规范的方法。// IRepository.cs (部分代码) using System.Linq.Expressions; namespace YourProject.Interfaces { public interface IRepositoryT where T : class { // ... 其他方法 (Add, Update, Delete, GetById等) // 基于规范获取单个实体 TaskT? GetSingleBySpecAsync(ISpecificationT spec, CancellationToken cancellationToken default); // 基于规范获取列表 TaskListT GetListBySpecAsync(ISpecificationT spec, CancellationToken cancellationToken default); // 基于规范计数 Taskint CountBySpecAsync(ISpecificationT spec, CancellationToken cancellationToken default); } }然后在仓储实现类中调用我们之前写的SpecificationEvaluator。// EfRepository.cs (部分代码) using Microsoft.EntityFrameworkCore; using YourProject.Specifications; using YourProject.Interfaces; namespace YourProject.Data { public class EfRepositoryT : IRepositoryT where T : class { protected readonly DbContext _dbContext; protected readonly DbSetT _dbSet; public EfRepository(DbContext dbContext) { _dbContext dbContext; _dbSet _dbContext.SetT(); } public virtual async TaskT? GetSingleBySpecAsync(ISpecificationT spec, CancellationToken cancellationToken default) { return await ApplySpecification(spec).FirstOrDefaultAsync(cancellationToken); } public virtual async TaskListT GetListBySpecAsync(ISpecificationT spec, CancellationToken cancellationToken default) { return await ApplySpecification(spec).ToListAsync(cancellationToken); } public virtual async Taskint CountBySpecAsync(ISpecificationT spec, CancellationToken cancellationToken default) { return await ApplySpecification(spec, false).CountAsync(cancellationToken); // false 表示不应用分页和排序 } // 核心方法将规范应用到 DbSet private IQueryableT ApplySpecification(ISpecificationT spec, bool applyPagingAndSorting true) { // 从 DbSet 开始 var query _dbSet.AsQueryable(); // 使用评估器 query SpecificationEvaluatorT.GetQuery(query, spec); // 注意对于 Count 操作我们通常不需要排序和分页 if (!applyPagingAndSorting) { // 可以创建一个不包含排序和分页逻辑的临时评估逻辑这里简单返回应用了Where和Include的查询 // 更严谨的做法是修改评估器或规范接口 return query; } return query; } } }6.2 在业务服务中调用现在在业务服务层使用规范变得非常直观和语义化。// ProductService.cs using YourProject.Specifications; namespace YourProject.Services { public class ProductService { private readonly IRepositoryProduct _productRepository; public ProductService(IRepositoryProduct productRepository) { _productRepository productRepository; } public async TaskListProduct GetActiveProductsAsync() { // 创建规范实例 var spec new ActiveProductSpecification(); // 使用仓储方法查询 var activeProducts await _productRepository.GetListBySpecAsync(spec); return activeProducts; } } }对比一下如果没有规范模式你可能需要在服务层写_dbContext.Products.Where(p p.IsActive).Include(p p.Category).OrderBy(p p.CreatedDate).ToListAsync()。当这个逻辑在多个地方出现时规范模式的优势就体现出来了。7. 高级功能规范组合与构建器规范模式的强大之处在于组合。我们可以创建更复杂的规范而无需修改现有代码。7.1 组合规范And, Or, Not我们需要创建一些扩展方法或新的规范类来支持逻辑组合。这里展示一种使用表达式树组合器的实现方式。首先需要一个表达式组合器工具类// ExpressionCombiner.cs using System.Linq.Expressions; namespace YourProject.Specifications { public static class ExpressionCombiner { public static ExpressionFuncT, bool AndT( ExpressionFuncT, bool left, ExpressionFuncT, bool right) { if (left null) return right; if (right null) return left; var parameter Expression.Parameter(typeof(T)); var leftVisitor new ReplaceExpressionVisitor(left.Parameters[0], parameter); var leftExpr leftVisitor.Visit(left.Body); var rightVisitor new ReplaceExpressionVisitor(right.Parameters[0], parameter); var rightExpr rightVisitor.Visit(right.Body); return Expression.LambdaFuncT, bool( Expression.AndAlso(leftExpr, rightExpr), parameter); } public static ExpressionFuncT, bool OrT( ExpressionFuncT, bool left, ExpressionFuncT, bool right) { // 实现类似 And使用 Expression.OrElse // 为简洁省略实际项目需补充 throw new NotImplementedException(); } private class ReplaceExpressionVisitor : ExpressionVisitor { private readonly Expression _oldValue; private readonly Expression _newValue; public ReplaceExpressionVisitor(Expression oldValue, Expression newValue) { _oldValue oldValue; _newValue newValue; } public override Expression Visit(Expression node) { return node _oldValue ? _newValue : base.Visit(node); } } } }然后可以创建支持组合的规范基类或扩展方法// CompositeSpecification.cs namespace YourProject.Specifications { public abstract class CompositeSpecificationT : BaseSpecificationT { protected CompositeSpecification(ExpressionFuncT, bool criteria) : base(criteria) { } public CompositeSpecificationT And(ISpecificationT other) { var combinedCriteria ExpressionCombiner.And(this.Criteria, other.Criteria); // 注意这里简化处理实际组合时还需考虑 Includes 等的合并 // 可以返回一个新的规范实例或者修改当前实例的 Criteria // 这里演示返回新实例的思路 return new DynamicSpecificationT(combinedCriteria); } } // 一个用于动态组合的规范类 public class DynamicSpecificationT : CompositeSpecificationT { public DynamicSpecification(ExpressionFuncT, bool criteria) : base(criteria) { } } }7.2 使用规范构建器Specification Builder对于动态查询如前端传入的多个过滤条件使用构建器模式可以更优雅地创建规范。// ProductSpecificationBuilder.cs namespace YourProject.Specifications { public class ProductSpecificationBuilder { private ExpressionFuncProduct, bool _criteria p true; // 初始为 true private readonly ListExpressionFuncProduct, object _includes new(); private ExpressionFuncProduct, object _orderBy; private bool _orderByDesc; private int? _skip; private int? _take; public ProductSpecificationBuilder WithNameContaining(string keyword) { if (!string.IsNullOrWhiteSpace(keyword)) { _criteria ExpressionCombiner.And(_criteria, p p.Name.Contains(keyword)); } return this; } public ProductSpecificationBuilder WithPriceInRange(decimal? minPrice, decimal? maxPrice) { if (minPrice.HasValue) { _criteria ExpressionCombiner.And(_criteria, p p.Price minPrice.Value); } if (maxPrice.HasValue) { _criteria ExpressionCombiner.And(_criteria, p p.Price maxPrice.Value); } return this; } public ProductSpecificationBuilder WithCategoryId(int? categoryId) { if (categoryId.HasValue) { _criteria ExpressionCombiner.And(_criteria, p p.CategoryId categoryId.Value); } return this; } public ProductSpecificationBuilder IncludeCategory() { _includes.Add(p p.Category); return this; } public ProductSpecificationBuilder OrderByPrice(bool descending false) { _orderBy p p.Price; _orderByDesc descending; return this; } public ProductSpecificationBuilder Paginate(int page, int pageSize) { _skip (page - 1) * pageSize; _take pageSize; return this; } public ISpecificationProduct Build() { var spec new DynamicSpecificationProduct(_criteria); // 这里需要将_includes, _orderBy等设置到spec中需要扩展DynamicSpecification或使用新类 // 为演示简洁假设有一个方法可以设置这些属性 // spec.SetIncludes(_includes); // if (_orderBy ! null) spec.ApplyOrderBy(_orderBy); // if (_skip.HasValue _take.HasValue) spec.ApplyPaging(_skip.Value, _take.Value); return spec; } } }在服务层中使用构建器public async TaskListProduct SearchProductsAsync(string keyword, decimal? minPrice, decimal? maxPrice, int? categoryId, int page, int pageSize) { var specBuilder new ProductSpecificationBuilder() .WithNameContaining(keyword) .WithPriceInRange(minPrice, maxPrice) .WithCategoryId(categoryId) .IncludeCategory() .OrderByPrice(descending: true) .Paginate(page, pageSize); var spec specBuilder.Build(); return await _productRepository.GetListBySpecAsync(spec); }这种链式调用的方式让动态查询的构建过程非常清晰。8. 功能测试与效果验证如何验证我们的规范模式实现是正确且高效的呢我们可以从单元测试和集成测试两个层面进行。8.1 单元测试测试规范本身规范是纯内存对象不依赖数据库非常适合单元测试。// ActiveProductSpecificationTests.cs using Xunit; using YourProject.Specifications; namespace YourProject.Tests.Unit.Specifications { public class ActiveProductSpecificationTests { [Fact] public void Criteria_Should_Filter_Active_Products() { // Arrange var spec new ActiveProductSpecification(); var products new ListProduct { new Product { Id 1, Name Active Product, IsActive true }, new Product { Id 2, Name Inactive Product, IsActive false }, new Product { Id 3, Name Another Active, IsActive true } }.AsQueryable(); // Act var filtered products.Where(spec.Criteria.Compile()); // 编译表达式在内存中测试 // Assert Assert.Equal(2, filtered.Count()); Assert.All(filtered, p Assert.True(p.IsActive)); } [Fact] public void Includes_Should_Contain_Category() { // Arrange var spec new ActiveProductSpecification(); // Act Assert Assert.Single(spec.Includes); // 可以通过检查表达式树来验证这里简单断言数量 } } }8.2 集成测试测试仓储与规范的结合使用内存数据库如 EF Core 的 In-Memory Database 或 SQLite来测试规范与数据库的交互。// ProductRepositorySpecificationTests.cs using Microsoft.EntityFrameworkCore; using YourProject.Data; using YourProject.Specifications; using Xunit; namespace YourProject.Tests.Integration.Data { public class ProductRepositorySpecificationTests { private readonly DbContextOptionsYourDbContext _dbContextOptions; public ProductRepositorySpecificationTests() { _dbContextOptions new DbContextOptionsBuilderYourDbContext() .UseInMemoryDatabase(databaseName: $TestDb_{Guid.NewGuid()}) // 唯一名避免冲突 .Options; } [Fact] public async Task GetListBySpecAsync_WithActiveSpec_Should_Return_Only_Active_Products() { // Arrange await using var context new YourDbContext(_dbContextOptions); var repository new EfRepositoryProduct(context); // 添加测试数据 context.Products.AddRange( new Product { Id 1, Name P1, IsActive true }, new Product { Id 2, Name P2, IsActive false }, new Product { Id 3, Name P3, IsActive true } ); await context.SaveChangesAsync(); var spec new ActiveProductSpecification(); // Act var result await repository.GetListBySpecAsync(spec); // Assert Assert.Equal(2, result.Count); Assert.All(result, p Assert.True(p.IsActive)); Assert.Contains(result, p p.Id 1); Assert.Contains(result, p p.Id 3); Assert.DoesNotContain(result, p p.Id 2); } } }8.3 验证生成的 SQL为了确保规范模式没有引入性能问题最关键的一步是检查它生成的 SQL 是否合理。在开发或测试环境中启用 EF Core 的日志记录。// 在 Startup.cs 或 Program.cs 中配置 builder.Services.AddDbContextYourDbContext(options options.UseSqlServer(connectionString) .LogTo(Console.WriteLine, LogLevel.Information) // 输出到控制台 .EnableSensitiveDataLogging()); // 可选显示参数值运行一个使用复杂组合规范的查询观察控制台输出的 SQL。它应该是一条整合了所有WHERE、JOIN(来自Include)、ORDER BY和OFFSET FETCH的语句与手写 LINQ 生成的 SQL 一致。如果出现了N1查询或客户端评估警告就需要检查规范中Include的使用或表达式是否正确。9. 常见问题与排查方法在实现和使用规范模式时你可能会遇到一些典型问题。下表列出了常见现象、原因和解决方案。问题现象可能原因排查方式解决方案编译错误无法将表达式树转换为 SQL在规范Criteria中使用了 C# 方法或逻辑这些方法无法被 EF Core 的查询提供程序翻译。检查Criteria表达式中的方法调用如.ToString(),.Substring()或复杂的逻辑运算。查看 EF Core 异常信息。1. 确保表达式中的操作是 EF Core 支持的。2. 将无法翻译的逻辑移到查询之外先获取数据到内存再处理。3. 使用IEnumerable后在内存中过滤仅适用于小数据集。查询性能慢日志显示多条 SQL规范中可能遗漏了必要的Include导致延迟加载产生 N1 查询。或者规范组合导致索引失效。1. 检查 EF Core 日志看是否在执行循环查询。2. 使用 SQL Server Profiler 或数据库监控工具分析生成的 SQL 执行计划。1. 在规范中添加必要的AddInclude。2. 检查组合查询的WHERE条件是否使用了索引列。3. 考虑使用AsNoTracking()如果不需要变更跟踪。分页结果不正确1. 排序规则不明确导致分页数据不稳定。2.Skip和Take在规范中设置错误。1. 检查规范是否设置了OrderBy。2. 在分页前确保有确定的排序顺序。3. 调试查看spec.Skip和spec.Take的值。1.始终为分页查询指定排序OrderBy或OrderByDescending。2. 验证分页参数计算逻辑如(page-1)*pageSize。规范组合And/Or后查询结果为空或错误表达式组合器逻辑有误组合后的表达式树结构不正确。编写单元测试用简单的内存数据测试组合规范。使用调试器查看组合后的Criteria表达式树。仔细检查ExpressionCombiner中的访问者模式实现确保参数替换正确。考虑使用成熟的第三方库如LinqKit的PredicateBuilder。单元测试时 Mock 规范困难ISpecificationT接口包含表达式树属性Mock 起来较复杂。-1. 改为测试具体规范类如ActiveProductSpecification它们是纯逻辑易测试。2. 如果必须 Mock可以使用Moq库的.SetupGet来设置属性返回值。“SpecificationEvaluator”中应用 Include 后排序/分页属性被忽略在GetQuery方法中Aggregate处理Include后返回的IQueryable可能丢失了原始查询的某些上下文虽然不常见。或者自定义的ApplySpecification方法逻辑有误。检查SpecificationEvaluator.GetQuery方法的执行顺序和返回结果。确保SpecificationEvaluator中各个步骤Where, Include, OrderBy, Skip/Take是顺序执行且作用于同一个query变量。参考 Ardalis.Specification 等开源库的实现。10. 最佳实践与使用建议为了让规范模式在你的项目中发挥最大价值遵循以下最佳实践命名清晰规范类名应明确表达其业务意图如ProductInStockSpecification、UserWithRoleSpecification、OrdersFromLastWeekSpecification。保持单一职责一个规范最好只封装一个独立的业务条件。复杂的查询通过组合多个简单规范来实现而不是创建一个庞大的“万能”规范。谨慎使用 Include只在确实需要立即加载关联数据时才在规范中添加Include。过度使用Include会导致查询复杂和性能下降。考虑使用投影Select来只获取需要的字段。为分页强制排序这是一个黄金法则。没有确定排序顺序的分页查询其结果顺序是不确定的可能导致数据重复或丢失。总是在分页规范中设置OrderBy。考虑缓存规范实例如果某个规范是无状态的即其Criteria不依赖运行时参数可以将其创建为静态只读实例避免重复创建。public static class ProductSpecifications { public static readonly ISpecificationProduct ActiveProducts new ActiveProductSpecification(); }与 CQRS 结合在 CQRS 架构中规范模式非常适合用在查询端Query Side用来构建复杂的查询对象。不要滥用如前所述对于简单查询直接使用 LINQ 更合适。规范模式是应对复杂性和提升可维护性的工具而不是所有查询的标配。团队共识在团队中推广使用前确保所有成员理解其原理和优势并建立一致的实现规范。规范模式是清理 EF Core 查询代码库的一把利器。它通过将查询条件提升为一等公民显著提升了代码的可读性、可复用性和可测试性。虽然初期需要投入时间搭建基础设施但对于中大型项目或查询逻辑复杂的场景这笔投资会在长期的维护和扩展中带来丰厚的回报。建议你从项目中最混乱的一处查询逻辑开始尝试用规范模式重构它亲身体验其带来的整洁与便利。