mcpField
E
entity-framework-core

entity-framework-core

github.com/managedcode数据与数据库49677 次安装1 天前更新
代码仓库

关于 entity-framework-core

entity-framework-core 是用于构建和审查 EF Core 数据访问代码的开发技能,涵盖 DbContext、查询、迁移及性能决策,并提供数据库应用开发中的模式与反模式。

主要功能

  1. 01DbContext 生命周期与配置指导
  2. 02查询跟踪、加载和投影模式
  3. 03迁移与架构变更实践
  4. 04性能、索引和并发指导
  5. 05检查生成的 SQL 与 N+1 查询

适用场景

  1. 01设计 EF Core 模型和查询
  2. 02审查迁移与 DbContext 用法
  3. 03排查慢查询和 N+1 行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/managedcode/dotnet-skills --skill entity-framework-core

安装后在 Claude Code 中输入/entity-framework-core确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

manifest.json
references/
anti-patterns.md
patterns.md

详细介绍

Entity Framework Core

适用场景

  • 使用 DbContext、迁移、模型配置或 EF 查询
  • 审查跟踪、加载、性能或事务行为
  • 将数据访问从 EF6 或自定义存储库迁移到 EF Core
  • 优化缓慢的数据库查询

文档

参考资料
  • patterns.md - 查询模式、跟踪策略、加载策略、投影、编译查询、分页和时态表
  • anti-patterns.md - 常见 EF Core 错误,包括 N+1 查询、大型上下文、通用存储库和缺少索引

工作流

  1. 新开发优先使用 EF Core,除非有记录在案的缺口需要使用 Dapper 或原始 SQL
  2. 将 DbContext 的生命周期限定为作用域——与工作单元保持一致
  3. 检查查询转换——检查生成的 SQL,避免 N+1 查询
  4. 将迁移视为一等事项——应可供审查,而非用完即弃
  5. 审慎处理提供程序行为——跨提供程序可用,但行为并不完全相同
  6. 通过检查查询进行验证——不要只依赖脑中的内存模型

上游最新说明

  • EF Core v10.0.11 是一个维护版本。它修复了 Azure SQL 兼容级别 170 的 JSON 转换问题,使嵌套的 OPENJSON 投影保留 AS JSON,并确保 EF 与 .NET 11 新增的 MemoryExtensions.Min/Max 重载保持兼容。升级后,请重新运行基于提供程序的 JSON 查询,并针对仓库选定的目标框架进行编译。
  • 2026 年 8 月的 EF Core 与 EF6 对比仍是迁移决策的首要参考。EF Core 是当前活跃的跨平台技术栈,但对于仅使用 EF6 且大量依赖 EDMX/ObjectContext 的代码,在完成特性清单并通过基于数据库的等效性测试之前,不应迁移。

DbContext 模式

基本配置
public class AppDbContext : DbContext
{
    public DbSet<Product> Products => Set<Product>();
    public DbSet<Order> Orders => Set<Order>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
    }
}

// Entity Configuration (Fluent API)
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Name).HasMaxLength(200).IsRequired();
        builder.HasIndex(p => p.Sku).IsUnique();
        builder.HasMany(p => p.OrderItems).WithOne(oi => oi.Product);
    }
}
使用 DI 注册
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(connectionString)
           .EnableSensitiveDataLogging()  // Dev only
           .EnableDetailedErrors());      // Dev only

// Or with pooling (better performance)
builder.Services.AddDbContextPool<AppDbContext>(options =>
    options.UseSqlServer(connectionString));

查询模式

只读时使用 AsNoTracking
// Bad - tracks entities unnecessarily
var products = await db.Products.ToListAsync();

// Good - no tracking overhead
var products = await db.Products
    .AsNoTracking()
    .ToListAsync();
投影到 DTO
// Bad - loads entire entity graph
var orders = await db.Orders
    .Include(o => o.Items)
    .Include(o => o.Customer)
    .ToListAsync();

// Good - loads only needed data
var orders = await db.Orders
    .Select(o => new OrderDto
    {
        Id = o.Id,
        CustomerName = o.Customer.Name,
        ItemCount = o.Items.Count,
        Total = o.Items.Sum(i => i.Price)
    })
    .ToListAsync();
避免 N+1 查询
// Bad - N+1 problem
foreach (var order in orders)
{
    var items = await db.OrderItems
        .Where(i => i.OrderId == order.Id)
        .ToListAsync();
}

// Good - eager loading
var orders = await db.Orders
    .Include(o => o.Items)
    .ToListAsync();

// Good - split query for large graphs
var orders = await db.Orders
    .Include(o => o.Items)
    .AsSplitQuery()
    .ToListAsync();
编译查询(EF Core 9)
// Pre-compiled for frequently used queries
private static readonly Func<AppDbContext, int, Task<Product?>> GetProductById =
    EF.CompileAsyncQuery((AppDbContext db, int id) =>
        db.Products.FirstOrDefault(p => p.Id == id));

// Usage
var product = await GetProductById(db, productId);

迁移模式

创建迁移
# Add migration
dotnet ef migrations add AddProductIndex

# Apply to database
dotnet ef database update

# Generate SQL script
dotnet ef migrations script --idempotent -o migrate.sql
数据迁移
public partial class AddProductIndex : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateIndex(
            name: "IX_Products_Sku",
            table: "Products",
            column: "Sku",
            unique: true);

        // Data migration (if needed)
        migrationBuilder.Sql(@"
            UPDATE Products
            SET NormalizedName = UPPER(Name)
            WHERE NormalizedName IS NULL");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropIndex(
            name: "IX_Products_Sku",
            table: "Products");
    }
}

应避免的反模式

反模式问题所在更好的做法
ToList() 后再筛选将所有数据加载到内存在查询中筛选
每个请求使用多个 DbContext事务问题作用域生命周期
到处使用延迟加载N+1 查询显式使用 Include
通用存储库包装器削弱查询能力直接使用 DbContext
忽略生成的 SQL隐藏的性能问题记录并审查
在循环中调用 SaveChanges()多次往返批量处理后再保存

性能最佳实践

  1. 为经常查询的列建立索引:

    builder.HasIndex(p => p.CreatedAt);
    builder.HasIndex(p => new { p.Category, p.Status });
    
  2. 使用分页:

    var page = await db.Products
        .OrderBy(p => p.Id)
        .Skip(pageSize * pageNumber)
        .Take(pageSize)
        .ToListAsync();
    
  3. 批量更新(EF Core 7+):

    await db.Products
        .Where(p => p.Category == "Obsolete")
        .ExecuteDeleteAsync();
    
    await db.Products
        .Where(p => p.Category == "Sale")
        .ExecuteUpdateAsync(p => p.SetProperty(x => x.Price, x => x.Price * 0.9m));
    
  4. 尽量减少网络往返:

    // Bad - 3 roundtrips
    var product = await db.Products.FindAsync(id);
    var reviews = await db.Reviews.Where(r => r.ProductId == id).ToListAsync();
    var related = await db.Products.Where(p => p.Category == product.Category).ToListAsync();
    
    // Good - 1 roundtrip
    var data = await db.Products
        .Where(p => p.Id == id)
        .Select(p => new
        {
            Product = p,
            Reviews = p.Reviews,
            Related = db.Products.Where(r => r.Category == p.Category).Take(5)
        })
        .FirstOrDefaultAsync();
    

并发模式

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ConcurrencyCheck]
    public int Version { get; set; }

    // Or use RowVersion
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

// Handle concurrency conflicts
try
{
    await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var databaseValues = await entry.GetDatabaseValuesAsync();
    // Resolve conflict...
}

交付内容

  • 与领域相匹配的 EF Core 模型和查询
  • 更安全的迁移和生命周期管理
  • 考虑性能的数据访问决策
  • 正确的索引和查询优化

验证

  • 查询行为符合预期(检查 SQL 日志)
  • 迁移可供审查且正确
  • 常见路径中没有 N+1 查询
  • 筛选或排序列上存在索引
  • DbContext 生命周期正确限定为作用域
  • 关键实体的并发得到妥善处理

评论

支持 Markdown