Token导航 LogoToken导航

Agent Skill · 前端设计

dotnet-efcore-architecture(dotnet efcore 架构)

dotnet-efcore-architecture 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

dotnet-efcore-architecture 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描有未通过项,按安装量在前端设计类的 10,216 个 Skill 里排第 6,501。

总安装 494周安装 20GitHub Stars 15下载 155核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dotnet-efcore-architecture(dotnet efcore 架构)
来源仓库:https://github.com/wshaddix/dotnet-skills
仓库路径:skills/dotnet-efcore-architecture
安装命令:
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-efcore-architecture
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-efcore-architecture

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

该技能提供 EF Core 数据层的战略架构模式,指导读写模型分离设计。适用于大型应用的数据访问层结构化和 N+1 查询治理场景。核心能力包括聚合边界划分、投影模式优化和行数限制强制执行。使用时应避免与 DbContext 生命周期管理等战术用法混淆。安装前需确认项目已规划好数据访问策略和性能目标。

来源仓库内路径:skills/dotnet-efcore-architecture

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-efcore-a…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

dotnet-efcore-architecture 怎么安装?

在终端执行 npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-efcore-architectur…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

dotnet-efcore-architecture 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

dotnet-efcore-architecture 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

dotnet-efcore-architecture 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

dotnet-efcore-architecture 用的人多吗?

公开数据是累计安装 494 次,近一周安装 20 次,下载 155 次,GitHub 15 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

dotnet-efcore-architecture 安全吗?

第三方扫描有未通过项,风险较高。除非你确认过源码,否则不建议直接安装到常用环境。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
dotnet-efcore-architecture

dotnet efcore 架构 · 本页

49415需要联网未明确未通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

dotnet-efcore-architecture

Strategic architectural patterns for EF Core data layers. Covers read/write model separation, aggregate boundary design, repository vs direct DbContext policy, N+1 query governance, row limit enforcement, and projection patterns. These patterns guide how to structure a data layer -- not how to write individual queries (see [skill:dotnet-efcore-patterns] for tactical usage).

Out of scope: Tactical EF Core usage (DbContext lifecycle, AsNoTracking, migrations, interceptors, compiled queries) is covered in [skill:dotnet-efcore-patterns]. Choosing between EF Core, Dapper, and ADO.NET is covered in [skill:dotnet-data-access-strategy]. DI container mechanics -- see [skill:dotnet-csharp-dependency-injection]. Async patterns -- see [skill:dotnet-csharp-async-patterns]. Integration testing data layers -- see [skill:dotnet-integration-testing] for database fixture and Testcontainers patterns. CI/CD pipelines -- see [skill:dotnet-gha-patterns] and [skill:dotnet-ado-patterns].

Cross-references: [skill:dotnet-efcore-patterns] for tactical DbContext usage and migrations, [skill:dotnet-data-access-strategy] for technology selection, [skill:dotnet-csharp-dependency-injection] for service registration, [skill:dotnet-csharp-async-patterns] for async query patterns.


Package Prerequisites

Examples in this skill use PostgreSQL (UseNpgsql). Substitute the provider package for your database:

DatabaseProvider Package
PostgreSQLNpgsql.EntityFrameworkCore.PostgreSQL
SQL ServerMicrosoft.EntityFrameworkCore.SqlServer
SQLiteMicrosoft.EntityFrameworkCore.Sqlite

All examples also require the core Microsoft.EntityFrameworkCore package (pulled in transitively by provider packages).


Read/Write Model Separation

Separate read models (queries) from write models (commands) to optimize each path independently. This is not full CQRS -- it is a practical separation using EF Core features.

Approach: Separate DbContext Types

// Write context: full change tracking, navigation properties, interceptors
public sealed class WriteDbContext : DbContext
{
    public DbSet<Order> Orders => Set<Order>();
    public DbSet<Product> Products => Set<Product>();

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

// Read context: no-tracking by default, optimized for projections
public sealed class ReadDbContext : DbContext
{
    public DbSet<Order> Orders => Set<Order>();
    public DbSet<Product> Products => Set<Product>();

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

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // Note: this is supplemental -- primary config is in DI registration
    }
}

Registration

// Write context: standard tracking, connection resiliency
builder.Services.AddDbContext<WriteDbContext>(options =>
    options.UseNpgsql(connectionString, npgsql =>
        npgsql.EnableRetryOnFailure(maxRetryCount: 3)));

// Read context: no-tracking, optionally pointed at a read replica
builder.Services.AddDbContext<ReadDbContext>(options =>
    options.UseNpgsql(readReplicaConnectionString ?? connectionString)
           .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

When to Separate

ScenarioRecommendation
Simple CRUD appSingle DbContext with per-query AsNoTracking()
Read-heavy API with complex queriesSeparate read/write contexts
Read replica databaseSeparate contexts with different connection strings
CQRS architectureSeparate contexts, possibly separate models

Start simple. Use a single DbContext and per-query AsNoTracking() until you have a concrete reason to split (different connection strings, divergent model shapes, or query complexity that justifies dedicated read models).


Aggregate Boundaries

An aggregate is a cluster of entities that are always loaded and saved together as a consistency boundary. EF Core maps well to aggregate-oriented design when navigation properties follow aggregate boundaries.

Defining Aggregates

// Order is the aggregate root -- it owns OrderItems
public sealed class Order
{
    public int Id { get; private set; }
    public string CustomerId { get; private set; } = default!;
    public OrderStatus Status { get; private set; }
    public DateTimeOffset CreatedAt { get; private set; }

    // Owned collection -- part of the Order aggregate
    private readonly List<OrderItem> _items = [];
    public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();

    public void AddItem(int productId, int quantity, decimal unitPrice)
    {
        if (Status != OrderStatus.Draft)
            throw new InvalidOperationException("Cannot add items to a non-draft order.");

        _items.Add(new OrderItem(productId, quantity, unitPrice));
    }
}

// OrderItem belongs to the Order aggregate -- no independent access
public sealed class OrderItem
{
    public int Id { get; private set; }
    public int ProductId { get; private set; }
    public int Quantity { get; private set; }
    public decimal UnitPrice { get; private set; }

    internal OrderItem(int productId, int quantity, decimal unitPrice)
    {
        ProductId = productId;
        Quantity = quantity;
        UnitPrice = unitPrice;
    }

    private OrderItem() { } // EF Core constructor
}

EF Core Configuration for Aggregates

public sealed class OrderConfiguration : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> builder)
    {
        builder.HasKey(o => o.Id);

        builder.Property(o => o.CustomerId).IsRequired().HasMaxLength(50);
        builder.Property(o => o.Status).HasConversion<string>();

        // Owned collection navigation -- cascade delete, no independent DbSet
        builder.OwnsMany(o => o.Items, items =>
        {
            items.WithOwner().HasForeignKey("OrderId");
            items.Property(i => i.ProductId).IsRequired();
        });

        // Alternatively, if OrderItem needs its own table with explicit FK:
        // builder.HasMany(o => o.Items)
        //     .WithOne()
        //     .HasForeignKey("OrderId")
        //     .OnDelete(DeleteBehavior.Cascade);
        //
        // builder.Navigation(o => o.Items)
        //     .UsePropertyAccessMode(PropertyAccessMode.Field);
    }
}

Aggregate Design Rules

  1. Load the entire aggregate -- do not load partial aggregates. Use Include() for the owned collections.
  2. Save through the aggregate root -- call SaveChangesAsync() on the root, not on child entities independently.
  3. Reference other aggregates by ID -- do not create navigation properties between aggregate roots. Use CustomerId (foreign key value), not Customer (navigation property).
  4. Keep aggregates small -- large aggregates cause lock contention and slow loads. If a collection grows unbounded (e.g., audit logs), it does not belong in the aggregate.
  5. One aggregate per transaction -- modifying multiple aggregates in a single transaction creates coupling. Use domain events or eventual consistency for cross-aggregate operations.

Repository Policy

Whether to use the repository pattern or access DbContext directly is a team decision. Both approaches are valid in.NET.

Option A: Direct DbContext Access

public sealed class CreateOrderHandler(WriteDbContext db)
{
    public async Task<int> HandleAsync(
        CreateOrderCommand command,
        CancellationToken ct)
    {
        var order = new Order(command.CustomerId);

        foreach (var item in command.Items)
        {
            order.AddItem(item.ProductId, item.Quantity, item.UnitPrice);
        }

        db.Orders.Add(order);
        await db.SaveChangesAsync(ct);

        return order.Id;
    }
}

Pros: Simple, no abstraction overhead, full LINQ power, easy to debug. Cons: Business logic can leak into query methods, harder to unit test without a database.

Option B: Repository per Aggregate Root

public interface IOrderRepository
{
    Task<Order?> GetByIdAsync(int id, CancellationToken ct);
    Task AddAsync(Order order, CancellationToken ct);
    Task SaveChangesAsync(CancellationToken ct);
}

public sealed class OrderRepository(WriteDbContext db) : IOrderRepository
{
    public async Task<Order?> GetByIdAsync(int id, CancellationToken ct)
    {
        return await db.Orders
            .Include(o => o.Items)
            .FirstOrDefaultAsync(o => o.Id == id, ct);
    }

    public async Task AddAsync(Order order, CancellationToken ct)
    {
        await db.Orders.AddAsync(order, ct);
    }

    public Task SaveChangesAsync(CancellationToken ct)
    {
        return db.SaveChangesAsync(ct);
    }
}

Pros: Testable without a database, encapsulates query logic, enforces aggregate loading rules. Cons: Extra abstraction layer, can become a leaky abstraction if LINQ is exposed, repository per aggregate can proliferate.

Decision Guide

FactorDirect DbContextRepository
Team sizeSmall, alignedLarge, varied experience
Test strategyIntegration tests with real DBUnit tests with mocked repos
Query complexityHigh (reports, projections)Low-medium (CRUD, aggregates)
Aggregate disciplineEnforced by conventionEnforced by interface

Do not create generic repositories (IRepository<T>). They add abstraction without value -- the generic interface cannot express aggregate-specific loading rules (which Includes to use, which filters to apply). Repository interfaces should be specific to the aggregate root they serve.


N+1 Query Governance

N+1 queries are the most common EF Core performance problem. They occur when code iterates over a collection and executes a query per element, instead of loading all data upfront.

Detection

Enable sensitive logging in development to see SQL queries:

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(connectionString)
           .LogTo(Console.WriteLine, LogLevel.Information)
           .EnableSensitiveDataLogging()  // Development only
           .EnableDetailedErrors());      // Development only

Common N+1 Patterns and Fixes

Pattern 1: Lazy loading in a loop

// BAD: N+1 -- each order.Items triggers a query
var orders = await db.Orders.ToListAsync(ct);
foreach (var order in orders)
{
    var total = order.Items.Sum(i => i.Quantity * i.UnitPrice); // Lazy load!
}

// GOOD: Eager load with Include
var orders = await db.Orders
    .Include(o => o.Items)
    .ToListAsync(ct);

Pattern 2: Querying inside a loop

// BAD: N+1 -- one query per customer
foreach (var customerId in customerIds)
{
    var orders = await db.Orders
        .Where(o => o.CustomerId == customerId)
        .ToListAsync(ct);
    // ...
}

// GOOD: Single query with Contains
var orders = await db.Orders
    .Where(o => customerIds.Contains(o.CustomerId))
    .ToListAsync(ct);

Pattern 3: Missing projection

// BAD: Loads full entity graph, then maps in memory
var orders = await db.Orders
    .Include(o => o.Items)
    .Include(o => o.Customer)
    .ToListAsync(ct);
var dtos = orders.Select(o => new OrderDto(...));

// GOOD: Project in the query -- no tracking, no extra data loaded
var dtos = await db.Orders
    .Select(o => new OrderDto
    {
        Id = o.Id,
        CustomerName = o.Customer.Name,
        ItemCount = o.Items.Count,
        Total = o.Items.Sum(i => i.Quantity * i.UnitPrice)
    })
    .ToListAsync(ct);

Governance Checklist

  • Disable lazy loading -- do not install Microsoft.EntityFrameworkCore.Proxies or configure UseLazyLoadingProxies(). Eager loading via Include() or projection via Select() makes data access explicit.
  • Review queries in code review -- look for loops that access navigation properties or call FindAsync / FirstOrDefaultAsync per element.
  • Use query tags -- db.Orders.TagWith("GetOrderSummary") makes queries identifiable in logs and profiling tools.
  • Set up EF Core logging in development -- every lazy load or unexpected query is visible in the console output.

Row Limits and Pagination

Unbounded queries are a production risk. Always limit the number of rows returned.

Keyset Pagination (Recommended)

Keyset pagination (also called cursor-based or seek pagination) is more efficient than offset pagination for large datasets:

public async Task<PagedResult<OrderSummary>> GetOrdersAsync(
    string customerId,
    int? afterId,
    int pageSize,
    CancellationToken ct)
{
    const int maxPageSize = 100;
    pageSize = Math.Min(pageSize, maxPageSize);

    var query = db.Orders
        .AsNoTracking()
        .Where(o => o.CustomerId == customerId);

    if (afterId.HasValue)
    {
        query = query.Where(o => o.Id > afterId.Value);
    }

    var items = await query
        .OrderBy(o => o.Id)
        .Take(pageSize + 1)  // Fetch one extra to detect "has next page"
        .Select(o => new OrderSummary
        {
            Id = o.Id,
            Status = o.Status,
            CreatedAt = o.CreatedAt,
            Total = o.Items.Sum(i => i.Quantity * i.UnitPrice)
        })
        .ToListAsync(ct);

    var hasNext = items.Count > pageSize;
    if (hasNext)
    {
        items.RemoveAt(items.Count - 1);
    }

    return new PagedResult<OrderSummary>
    {
        Items = items,
        HasNextPage = hasNext,
        NextCursor = hasNext ? items[^1].Id : null
    };
}

Offset Pagination (Simple Cases)

For admin UIs or small datasets where exact page numbers matter:

var page = await db.Orders
    .AsNoTracking()
    .OrderBy(o => o.CreatedAt)
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync(ct);

Warning: Offset pagination degrades at scale -- OFFSET 10000 forces the database to scan and discard 10,000 rows. Prefer keyset pagination for user-facing APIs.

Row Limit Enforcement

Set a hard upper bound on all queries to prevent accidental full-table scans:

// Interceptor approach: enforce max rows at the DbContext level
public sealed class RowLimitInterceptor : IQueryExpressionInterceptor
{
    private const int MaxRows = 1000;

    public Expression QueryCompilationStarting(
        Expression queryExpression,
        QueryExpressionEventData eventData)
    {
        // This is a simplified illustration -- actual implementation requires
        // expression tree analysis to detect existing Take() calls.
        // Consider using a code review rule or analyzer instead.
        return queryExpression;
    }
}

Practical approach: Rather than a runtime interceptor, enforce row limits through:

  1. Code review convention -- every ToListAsync() must have Take(N) or be a Select() projection with Take(N).
  2. API-level page size caps -- validate pageSize in the request pipeline before it reaches the query.
  3. Query tags -- annotate queries with TagWith() to identify unbounded queries in monitoring.

Projection Patterns

Projections (Select()) are the most effective optimization for read queries. They reduce data transfer, skip change tracking, and eliminate N+1 risks.

Typed Projections

public sealed record OrderSummary
{
    public int Id { get; init; }
    public string CustomerName { get; init; } = default!;
    public int ItemCount { get; init; }
    public decimal Total { get; init; }
    public DateTimeOffset CreatedAt { get; init; }
}

var summaries = await db.Orders
    .Select(o => new OrderSummary
    {
        Id = o.Id,
        CustomerName = o.Customer.Name,
        ItemCount = o.Items.Count,
        Total = o.Items.Sum(i => i.Quantity * i.UnitPrice),
        CreatedAt = o.CreatedAt
    })
    .OrderByDescending(o => o.CreatedAt)
    .Take(50)
    .ToListAsync(ct);

Advantages Over Entity Loading

ConcernEntity + IncludeProjection (Select)
Change trackingYes (unless AsNoTracking)No
Data transferredAll columnsOnly selected columns
N+1 riskYes (lazy nav props)No (computed in SQL)
Cartesian explosionYes (multiple Includes)No (single query)
Type safetyEntity typesDTO/record types

Rule: Use projections for all read-only endpoints that return DTOs. Reserve entity loading for commands that modify data.


Key Principles

  • Separate read and write paths when you have different optimization needs -- do not force a single model to serve both
  • Design aggregates around consistency boundaries -- not around database tables
  • Reference other aggregates by ID -- navigation properties between aggregate roots create coupling
  • Ban lazy loading -- make all data access explicit through Include() or Select()
  • Enforce row limits -- every query that returns a list must have an upper bound
  • Project early -- use Select() to push computation to the database and reduce data transfer
  • Prefer keyset pagination over offset pagination for scalability

Agent Gotchas

  1. Do not create navigation properties between aggregate roots -- use foreign key values (e.g., CustomerId) instead of navigation properties (e.g., Customer). Cross-aggregate navigation properties break the consistency boundary and encourage loading data that belongs to another aggregate.
  2. Do not create generic repositories (IRepository<T>) -- they cannot express aggregate-specific loading rules and become leaky abstractions. Create one repository interface per aggregate root with explicit methods.
  3. Do not use UseLazyLoadingProxies() -- lazy loading hides N+1 queries and makes performance unpredictable. Use Include() for eager loading or Select() for projections.
  4. Do not return IQueryable<T> from repositories -- it leaks persistence concerns to callers and makes query behavior unpredictable (e.g., multiple enumeration, client-side evaluation). Return materialized results (List<T>, T?).
  5. Do not write ToListAsync() without Take() on unbounded queries -- full table scans are a production incident waiting to happen. Always limit the result set.
  6. Do not put audit logs or event streams inside aggregates -- unbounded collections cause slow loads and lock contention. Model them as separate entities or dedicated stores.

References

平台分布

Codex

39.18%
按下载量换算61

Claude

30.77%
按下载量换算48

Cursor

17.84%
按下载量换算28

Gemini CLI

9.41%
按下载量换算15

安全审计

Gen Agent Trust Hub

通过

Socket

未通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills