Skip to content

预编译查询 Compiled Queries ​

目录 ​


什么是预编译查询 ​

概念理解 ​

预编译查询(Compiled Queries) 是将 LINQ 查询预先编译为可重用的委托,避免每次执行时重新解析和编译查询表达式树。

csharp
// ❌ 普通查询: 每次都编译
var products = await context.Products
    .Where(p => p.Price > 100)
    .ToListAsync();  // ← 每次都要编译查询

// ✅ 预编译查询: 编译一次,多次使用
private static readonly Func<AppDbContext, decimal, Task<List<Product>>> 
    GetExpensiveProducts = EF.CompileAsyncQuery(
        (AppDbContext context, decimal minPrice) =>
            context.Products.Where(p => p.Price > minPrice)
    );

// 使用预编译查询
var products = await GetExpensiveProducts(context, 100);  // ← 跳过编译,直接执行

工作原理 ​

mermaid
graph TB
    A[普通查询] --> B[解析表达式树]
    B --> C[生成 SQL]
    C --> D[执行查询]
    D --> E[返回结果]
    
    F[预编译查询] --> G{已编译?}
    G -->|首次| H[解析并编译]
    G -->|后续| I[直接使用]
    H --> J[缓存编译结果]
    J --> I
    I --> K[执行查询]
    K --> L[返回结果]

EF Core 查询编译过程 ​

普通查询执行流程:
┌─────────────────────────────────────┐
│ 1. 创建 LINQ 表达式树              │
│ 2. 遍历表达式树                     │
│ 3. 解析查询方法(Where, Select等)   │
│ 4. 转换为 SQL 表达式               │
│ 5. 参数化处理                       │
│ 6. 生成最终 SQL                    │
│ 7. 执行 SQL                        │
│ 8. 映射结果到对象                   │
└─────────────────────────────────────┘
     ↑
     每次执行都要重复 1-8 步!

预编译查询执行流程:
┌─────────────────────────────────────┐
│ 首次:                               │
│ 1. 编译查询(步骤 1-6)               │
│ 2. 缓存编译结果                     │
│                                     │
│ 后续:                               │
│ 1. 从缓存获取编译结果               │
│ 2. 执行 SQL(跳过 1-6)               │
│ 3. 映射结果到对象                   │
└─────────────────────────────────────┘
     ↑
     节省编译时间!

为什么需要预编译查询 ​

查询编译的开销 ​

EF Core 每次执行 LINQ 查询时,都需要进行以下操作:

  1. 表达式树遍历: 分析 LINQ 表达式结构
  2. 查询转换: 将 LINQ 转换为 SQL
  3. 参数提取: 处理查询中的变量和参数
  4. SQL 生成: 生成目标数据库的 SQL 语句
  5. 缓存查找: 检查是否已有编译缓存

对于高频执行的查询,编译开销可能占总执行时间的 20-40%!

性能影响示例 ​

csharp
// 场景: Web API 中频繁调用的查询
public class ProductsController : ControllerBase
{
    private readonly AppDbContext _context;
    
    // ❌ 普通查询: 每次请求都编译
    [HttpGet("expensive/{minPrice}")]
    public async Task<ActionResult<List<Product>>> GetExpensive(decimal minPrice)
    {
        var products = await _context.Products
            .Where(p => p.Price > minPrice)
            .ToListAsync();
        
        return Ok(products);
    }
    
    // 假设:
    // - 查询编译时间: 2ms
    // - 查询执行时间: 8ms
    // - 每秒 100 次请求
    // 
    // 总时间: (2ms + 8ms) × 100 = 1000ms/秒
    // 编译占比: 2ms / 10ms = 20%  ← 浪费!
}

// ✅ 预编译查询优化后
public class ProductsController : ControllerBase
{
    private static readonly Func<AppDbContext, decimal, Task<List<Product>>>
        GetExpensiveProducts = EF.CompileAsyncQuery(
            (AppDbContext context, decimal minPrice) =>
                context.Products.Where(p => p.Price > minPrice)
        );
    
    [HttpGet("expensive/{minPrice}")]
    public async Task<ActionResult<List<Product>>> GetExpensive(decimal minPrice)
    {
        var products = await GetExpensiveProducts(_context, minPrice);
        return Ok(products);
    }
    
    // 优化后:
    // - 查询编译时间: 0ms(跳过编译)
    // - 查询执行时间: 8ms
    // - 每秒 100 次请求
    // 
    // 总时间: 8ms × 100 = 800ms/秒
    // 性能提升: 20% 🚀
}

何时使用预编译查询 ​

✅ 适合使用:

  • 高频执行的查询(API 端点)
  • 循环中的查询
  • 定时任务中的重复查询
  • 热点代码路径

❌ 不适合使用:

  • 只执行一次的查询
  • 动态构建的复杂查询
  • 原型开发阶段
  • 性能不敏感的后台任务

如何使用预编译查询 ​

基本语法 ​

同步查询 ​

csharp
// 定义预编译查询
private static readonly Func<AppDbContext, int, Product> GetProductById = 
    EF.CompileQuery((AppDbContext context, int id) =>
        context.Products.FirstOrDefault(p => p.Id == id)
    );

// 使用
var product = GetProductById(context, 1);

异步查询 ​

csharp
// 定义预编译查询
private static readonly Func<AppDbContext, int, Task<Product>> GetProductByIdAsync = 
    EF.CompileAsyncQuery((AppDbContext context, int id) =>
        context.Products.FirstOrDefaultAsync(p => p.Id == id)
    );

// 使用
var product = await GetProductByIdAsync(context, 1);

完整示例 ​

示例 1: 简单过滤 ​

csharp
public class ProductQueries
{
    // 按价格范围查询
    public static readonly Func<AppDbContext, decimal, decimal, Task<List<Product>>> 
        GetProductsByPriceRange = EF.CompileAsyncQuery(
            (AppDbContext context, decimal minPrice, decimal maxPrice) =>
                context.Products
                    .Where(p => p.Price >= minPrice && p.Price <= maxPrice)
                    .OrderByDescending(p => p.Price)
                    .ToList()
        );
    
    // 按分类查询
    public static readonly Func<AppDbContext, int, Task<List<Product>>> 
        GetProductsByCategory = EF.CompileAsyncQuery(
            (AppDbContext context, int categoryId) =>
                context.Products
                    .Where(p => p.CategoryId == categoryId)
                    .OrderByDescending(p => p.CreatedAt)
                    .ToList()
        );
    
    // 搜索产品
    public static readonly Func<AppDbContext, string, Task<List<Product>>> 
        SearchProducts = EF.CompileAsyncQuery(
            (AppDbContext context, string searchTerm) =>
                context.Products
                    .Where(p => p.Name.Contains(searchTerm) 
                             || p.Description.Contains(searchTerm))
                    .Take(50)
                    .ToList()
        );
}

// 使用示例
public class ProductService
{
    private readonly AppDbContext _context;
    
    public async Task<List<Product>> GetElectronicsAsync()
    {
        return await ProductQueries.GetProductsByCategory(_context, 1);
    }
    
    public async Task<List<Product>> SearchLaptopsAsync()
    {
        return await ProductQueries.SearchProducts(_context, "laptop");
    }
}

示例 2: 包含关联数据 ​

csharp
// ⚠️ 注意: CompileQuery 不支持 Include
// 解决方案: 手动加载关联数据或使用投影

// 方式 1: 分别查询
public static readonly Func<AppDbContext, int, Task<Customer>> GetCustomerWithOrders = 
    EF.CompileAsyncQuery(
        (AppDbContext context, int customerId) =>
            context.Customers
                .FirstOrDefault(c => c.Id == customerId)
    );

public async Task<Customer> GetCustomerFullDataAsync(int customerId)
{
    var customer = await GetCustomerWithOrders(_context, customerId);
    
    if (customer != null)
    {
        // 手动加载订单
        await _context.Entry(customer)
            .Collection(c => c.Orders)
            .LoadAsync();
    }
    
    return customer;
}

// 方式 2: 使用投影(推荐)
public static readonly Func<AppDbContext, int, Task<CustomerDto>> GetCustomerDto = 
    EF.CompileAsyncQuery(
        (AppDbContext context, int customerId) =>
            context.Customers
                .Where(c => c.Id == customerId)
                .Select(c => new CustomerDto
                {
                    Id = c.Id,
                    Name = c.Name,
                    OrderCount = c.Orders.Count,
                    TotalSpent = c.Orders.Sum(o => o.TotalAmount)
                })
                .FirstOrDefault()
    );

示例 3: 分页查询 ​

csharp
// 分页查询(返回总数需要使用 OUT 参数或单独查询)
public static readonly Func<AppDbContext, string, int, int, Task<List<Product>>> 
    GetPagedProducts = EF.CompileAsyncQuery(
        (AppDbContext context, string searchTerm, int skip, int take) =>
            context.Products
                .Where(p => string.IsNullOrEmpty(searchTerm) 
                         || p.Name.Contains(searchTerm))
                .OrderByDescending(p => p.CreatedAt)
                .Skip(skip)
                .Take(take)
                .ToList()
    );

// 使用
var products = await GetPagedProducts(_context, "laptop", 0, 10);

示例 4: 批量更新 ​

csharp
// EF Core 8+ 批量操作也可以预编译
public static readonly Func<AppDbContext, decimal, Task<int>> IncreasePrices = 
    EF.CompileAsyncQuery(
        (AppDbContext context, decimal increaseRate) =>
            context.Products
                .Where(p => p.IsActive)
                .ExecuteUpdate(setters => setters
                    .SetProperty(p => p.Price, p => p.Price * increaseRate))
    );

// 使用
var affectedRows = await IncreasePrices(_context, 1.1m);
Console.WriteLine($"更新了 {affectedRows} 条记录");

高级用法 ​

1. 在仓储模式中使用 ​

csharp
public class ProductRepository : IProductRepository
{
    private readonly AppDbContext _context;
    
    // 静态字段: 所有实例共享
    private static readonly Func<AppDbContext, int, Task<Product>> GetById = 
        EF.CompileAsyncQuery(
            (AppDbContext context, int id) =>
                context.Products.FirstOrDefaultAsync(p => p.Id == id)
        );
    
    private static readonly Func<AppDbContext, Task<List<Product>>> GetAll = 
        EF.CompileAsyncQuery(
            (AppDbContext context) =>
                context.Products.OrderByDescending(p => p.CreatedAt).ToList()
        );
    
    public ProductRepository(AppDbContext context)
    {
        _context = context;
    }
    
    public async Task<Product> GetByIdAsync(int id)
    {
        return await GetById(_context, id);
    }
    
    public async Task<List<Product>> GetAllAsync()
    {
        return await GetAll(_context);
    }
}

2. 泛型预编译查询 ​

csharp
public class QueryCompiler<TContext> where TContext : DbContext
{
    private static readonly ConcurrentDictionary<string, Delegate> _compiledQueries 
        = new();
    
    public static Func<TContext, int, Task<TEntity>> CreateGetByIdQuery<TEntity>(
        Expression<Func<TEntity, int>> keySelector) where TEntity : class
    {
        var cacheKey = $"GetById_{typeof(TEntity).Name}";
        
        return (Func<TContext, int, Task<TEntity>>)_compiledQueries.GetOrAdd(
            cacheKey,
            _ => EF.CompileAsyncQuery(
                (TContext context, int id) =>
                    context.Set<TEntity>()
                        .FirstOrDefaultAsync(e => EF.Property<int>(e, "Id") == id)
            )
        );
    }
}

// 使用
var getProduct = QueryCompiler<AppDbContext>.CreateGetByIdQuery<Product>(p => p.Id);
var product = await getProduct(_context, 1);

3. 组合查询条件 ​

csharp
// 对于复杂条件,可以预编译多个简单查询并组合
public static class ProductQueries
{
    public static readonly Func<AppDbContext, int?, decimal?, DateTime?, Task<List<Product>>> 
        GetFilteredProducts = EF.CompileAsyncQuery(
            (AppDbContext context, int? categoryId, decimal? minPrice, DateTime? startDate) =>
            {
                var query = context.Products.AsQueryable();
                
                if (categoryId.HasValue)
                    query = query.Where(p => p.CategoryId == categoryId.Value);
                
                if (minPrice.HasValue)
                    query = query.Where(p => p.Price >= minPrice.Value);
                
                if (startDate.HasValue)
                    query = query.Where(p => p.CreatedAt >= startDate.Value);
                
                return query.OrderByDescending(p => p.CreatedAt).ToList();
            }
        );
}

// 使用: 灵活组合条件
var electronics = await ProductQueries.GetFilteredProducts(
    _context, 
    categoryId: 1, 
    minPrice: null, 
    startDate: null);

var expensiveRecent = await ProductQueries.GetFilteredProducts(
    _context, 
    categoryId: null, 
    minPrice: 100, 
    startDate: DateTime.Today.AddMonths(-1));

性能分析与基准测试 ​

基准测试代码 ​

csharp
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

[MemoryDiagnoser]
public class QueryCompilationBenchmark
{
    private AppDbContext _context;
    private static readonly Func<AppDbContext, int, Task<Product>> GetProductCompiled = 
        EF.CompileAsyncQuery(
            (AppDbContext context, int id) =>
                context.Products.FirstOrDefaultAsync(p => p.Id == id)
        );
    
    [GlobalSetup]
    public void Setup()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlServer("Server=...;Database=BenchmarkTest;...")
            .Options;
        
        _context = new AppDbContext(options);
    }
    
    [Benchmark]
    public async Task<Product> RegularQuery()
    {
        return await _context.Products
            .FirstOrDefaultAsync(p => p.Id == 1);
    }
    
    [Benchmark]
    public async Task<Product> CompiledQuery()
    {
        return await GetProductCompiled(_context, 1);
    }
    
    [Benchmark]
    public async Task<List<Product>> RegularQuery_List()
    {
        return await _context.Products
            .Where(p => p.Price > 100)
            .ToListAsync();
    }
    
    [Benchmark]
    public async Task<List<Product>> CompiledQuery_List()
    {
        return await GetExpensiveProducts(_context, 100);
    }
}

// 运行基准测试
var summary = BenchmarkRunner.Run<QueryCompilationBenchmark>();

典型测试结果 ​

BenchmarkDotNet v0.13.12

Method                  | Mean      | Error   | StdDev  | Gen0   | Allocated |
----------------------- |----------:|--------:|--------:|-------:|----------:|
RegularQuery            |  2.850 ms | 0.056ms | 0.065ms | 0.9766 |   8.2 KB  |
CompiledQuery           |  2.100 ms | 0.042ms | 0.045ms | 0.9766 |   8.2 KB  |
RegularQuery_List       |  5.200 ms | 0.103ms | 0.120ms | 2.9297 |  24.5 KB  |
CompiledQuery_List      |  4.350 ms | 0.087ms | 0.095ms | 2.9297 |  24.5 KB  |

结论:
- 单次查询: 预编译快 26% (2.85ms → 2.10ms)
- 列表查询: 预编译快 16% (5.20ms → 4.35ms)
- 内存分配: 相同

实际场景性能 ​

csharp
// 场景: Web API 高并发查询
public class PerformanceTest
{
    private readonly HttpClient _client;
    private readonly ILogger _logger;
    
    public async Task SimulateHighConcurrencyAsync()
    {
        var tasks = new List<Task>();
        var sw = Stopwatch.StartNew();
        
        // 模拟 1000 个并发请求
        for (int i = 0; i < 1000; i++)
        {
            tasks.Add(Task.Run(async () =>
            {
                var response = await _client.GetAsync("/api/products/expensive/100");
                return await response.Content.ReadFromJsonAsync<List<Product>>();
            }));
        }
        
        await Task.WhenAll(tasks);
        sw.Stop();
        
        _logger.LogInformation($"完成 1000 个请求耗时: {sw.ElapsedMilliseconds}ms");
        _logger.LogInformation($"平均每个请求: {sw.ElapsedMilliseconds / 1000.0}ms");
    }
}

// 测试结果:
// 普通查询:   1000 请求 / 3200ms = 312 req/s
// 预编译查询: 1000 请求 / 2500ms = 400 req/s
// 吞吐量提升: 28% 🚀

性能影响因素 ​

因素影响程度说明
查询复杂度高越复杂的查询,编译开销越大
执行频率高频率越高,收益越明显
参数数量中参数越多,编译越慢
关联数据中Include 增加编译时间
数据库类型低不同数据库编译时间略有差异

.NET 8/9/10 新特性 ​

.NET 8: 改进的查询编译 ​

csharp
// .NET 8 优化了复杂查询的编译性能
// 对于包含多个 Where/OrderBy 的查询,编译速度提升 15-25%

var products = await _context.Products
    .Where(p => p.Price > 100)
    .Where(p => p.IsActive)
    .OrderByDescending(p => p.CreatedAt)
    .ThenBy(p => p.Name)
    .ToListAsync();

// .NET 8 内部优化:
// - 更快的表达式树遍历
// - 改进的缓存策略
// - 减少不必要的内存分配

.NET 9: 增强的编译查询诊断 ​

csharp
// .NET 9 添加查询编译诊断信息
builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(connectionString)
           .EnableDetailedErrors()
           .LogTo(Console.WriteLine, LogLevel.Debug);
});

// 输出详细的编译信息:
// info: Compiling query model for 'Products.Where(p => p.Price > 100)'
// info: Query compilation took 2.5ms
// info: Using cached compiled query (cache hit)

.NET 10: 智能查询优化(预览) ​

预计特性:

  • 自动检测并优化热点查询
  • 基于 AI 的查询性能预测
  • 更智能的查询计划缓存
  • 运行时编译优化建议

最佳实践与陷阱 ​

最佳实践 ​

1. 识别热点查询 ​

csharp
// 监控慢查询日志
builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(connectionString)
           .LogTo((eventId, logLevel) => 
           {
               if (eventId.Id == RelationalEventId.CommandExecuted.Id)
                   return logLevel >= LogLevel.Warning;  // 记录慢查询
               return false;
           }, Console.WriteLine);
});

// 分析日志,找出高频查询
// 将这些查询转换为预编译形式

2. 合理组织预编译查询 ​

csharp
// ✅ 推荐: 集中管理
public static class CompiledQueries
{
    // Product 相关
    public static readonly Func<...> GetProductById = ...;
    public static readonly Func<...> GetProductsByCategory = ...;
    
    // Customer 相关
    public static readonly Func<...> GetCustomerById = ...;
    public static readonly Func<...> GetCustomerOrders = ...;
}

// ❌ 避免: 分散在各处
public class SomeService
{
    private static readonly Func<...> MyQuery = ...;  // 难以管理
}

3. 使用静态只读字段 ​

csharp
// ✅ 正确: static readonly
public static readonly Func<AppDbContext, int, Task<Product>> GetProduct = 
    EF.CompileAsyncQuery(...);

// ❌ 错误: 每次调用都重新编译
public Task<Product> GetProduct(AppDbContext ctx, int id)
{
    var compiled = EF.CompileAsyncQuery(...);  // 💥 失去预编译意义!
    return compiled(ctx, id);
}

4. 权衡利弊 ​

csharp
// 决策矩阵:
// 
// 查询频率 \ 复杂度  | 简单  | 中等  | 复杂
// ------------------|-------|-------|------
// 低频(每天几次)     | ❌    | ❌    | ⚠️
// 中频(每小时几次)   | ❌    | ⚠️   | ✅
// 高频(每秒几次)     | ✅    | ✅    | ✅
// 
// 建议: 优先预编译高频查询

常见陷阱 ​

陷阱 1: 过度使用 ​

csharp
// ❌ 错误: 为只执行一次的查询使用预编译
public async Task InitializeDatabaseAsync()
{
    // 这个查询只执行一次,不需要预编译
    var count = await _context.Products.CountAsync();
    
    if (count == 0)
    {
        // 种子数据也只插入一次
        await SeedDataAsync();
    }
}

陷阱 2: 动态查询无法预编译 ​

csharp
// ❌ 错误: 动态构建的查询无法预编译
public async Task<List<Product>> SearchAsync(Dictionary<string, object> filters)
{
    var query = _context.Products.AsQueryable();
    
    // 动态添加条件
    if (filters.ContainsKey("name"))
        query = query.Where(p => p.Name.Contains(filters["name"].ToString()));
    
    if (filters.ContainsKey("price"))
        query = query.Where(p => p.Price > Convert.ToDecimal(filters["price"]));
    
    // 💥 无法预编译,因为条件是动态的
    return await query.ToListAsync();
}

// ✅ 解决: 使用预定义的查询组合
public static readonly Func<AppDbContext, string, decimal, Task<List<Product>>> 
    SearchWithFilters = EF.CompileAsyncQuery(
        (AppDbContext context, string name, decimal minPrice) =>
            context.Products
                .Where(p => string.IsNullOrEmpty(name) || p.Name.Contains(name))
                .Where(p => minPrice <= 0 || p.Price > minPrice)
                .ToList()
    );

陷阱 3: 忘记异步版本 ​

csharp
// ❌ 错误: 在异步上下文中使用同步版本
public async Task<Product> GetProductAsync(int id)
{
    // 💥 会阻塞线程
    return GetProductById(_context, id);  
}

// ✅ 正确: 使用异步版本
public async Task<Product> GetProductAsync(int id)
{
    return await GetProductByIdAsync(_context, id);
}

陷阱 4: 生命周期问题 ​

csharp
// ❌ 错误: DbContext 生命周期问题
public class ProductService : IDisposable
{
    private AppDbContext _context;
    private static readonly Func<AppDbContext, int, Task<Product>> GetProduct = 
        EF.CompileAsyncQuery(...);
    
    public async Task<Product> GetByIdAsync(int id)
    {
        return await GetProduct(_context, id);
    }
    
    public void Dispose()
    {
        _context?.Dispose();  // ⚠️ 预编译查询仍然引用已释放的 context
    }
}

// ✅ 正确: 通过依赖注入管理生命周期
public class ProductService
{
    private readonly AppDbContext _context;
    
    public ProductService(AppDbContext context)
    {
        _context = context;
    }
    
    public async Task<Product> GetByIdAsync(int id)
    {
        return await GetProduct(_context, id);
    }
}

调试技巧 ​

1. 验证是否使用了预编译 ​

csharp
// 启用详细日志
builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(connectionString)
           .LogTo(Console.WriteLine, LogLevel.Information);
});

// 观察日志:
// 首次执行: "Compiling query..."
// 后续执行: "Using cached query plan" ← 确认预编译生效

2. 性能对比测试 ​

csharp
public class CompilationTest
{
    public async Task ComparePerformanceAsync()
    {
        var sw = new Stopwatch();
        
        // 测试普通查询
        sw.Start();
        for (int i = 0; i < 100; i++)
        {
            await _context.Products.FirstOrDefaultAsync(p => p.Id == 1);
        }
        sw.Stop();
        Console.WriteLine($"普通查询: {sw.ElapsedMilliseconds}ms");
        
        // 测试预编译查询
        sw.Restart();
        for (int i = 0; i < 100; i++)
        {
            await GetProductCompiled(_context, 1);
        }
        sw.Stop();
        Console.WriteLine($"预编译查询: {sw.ElapsedMilliseconds}ms");
    }
}

总结 ​

核心要点 ​

  1. 预编译查询: 编译一次,多次使用
  2. 性能提升: 15-30%,高频场景更明显
  3. 适用场景: 热点查询、API 端点、循环查询
  4. 使用方式: EF.CompileAsyncQuery
  5. 注意事项: 静态只读、合理组织、避免过度使用

性能对比 ​

查询类型首次执行后续执行适用场景
普通查询编译+执行编译+执行低频查询
预编译查询编译+执行仅执行高频查询

使用建议 ​

决策流程:
1. 识别高频查询(监控日志)
2. 评估查询复杂度
3. 创建预编译查询
4. 性能测试验证
5. 持续监控效果

代码模板 ​

csharp
// 标准模板
public static class CompiledQueries
{
    // GetById
    public static readonly Func<TContext, int, Task<TEntity>> GetById = 
        EF.CompileAsyncQuery(
            (TContext context, int id) =>
                context.Set<TEntity>().FirstOrDefaultAsync(e => e.Id == id)
        );
    
    // List
    public static readonly Func<TContext, Task<List<TEntity>>> GetAll = 
        EF.CompileAsyncQuery(
            (TContext context) =>
                context.Set<TEntity>().OrderByDescending(e => e.CreatedAt).ToList()
        );
    
    // Filtered
    public static readonly Func<TContext, TParam, Task<List<TEntity>>> GetByFilter = 
        EF.CompileAsyncQuery(
            (TContext context, TParam param) =>
                context.Set<TEntity>()
                    .Where(e => e.SomeProperty == param)
                    .ToList()
        );
}

下一步 ​

基于 MIT 许可发布