‹ 首页

abp-efcore

@burakdmir · 收录于 昨天 · 上游提交 2 周前

ABP Framework v10.x (10.4/10.5) Entity Framework Core: AbpDbContext, ConfigureByConvention, AddAbpDbContext, repository (EfCoreRepository), migration, PostgreSQL/MySQL/SQLite/Oracle. Use when working with EF Core, DbContext, migrations, or repository implementation in ABP.

适合你,如果在 ABP 项目中使用 Entity Framework Core 进行数据访问。

/ 通过 npx 安装 校验哈希
npx oh-my-skill add burakdmir/abp-skills/abp-efcore
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- burakdmir/abp-skills/abp-efcore
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify burakdmir/abp-skills/abp-efcore
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
13GitHub stars
~2.1K上下文体积 · 单文件
索引托管

怎么用

商店整理自技能原文 · 版本 ce71259 · 表述以原文为准
它做什么

装上后,当用户询问 ABP 框架中 Entity Framework Core 的使用方法时,Claude 能给出具体代码示例(如 DbContext、仓库、迁移)和配置指南,并说明最佳实践。

什么时候触发

当用户提到“ABP EF Core”、“DbContext”、“迁移”、“仓库”等关键词,或要求执行 EF Core 相关操作时触发。

装好后可以这样说
技能原文 SKILL.md作者撰写 · MIT · ce71259

ABP Framework — Entity Framework Core

A guide to EF Core integration in ABP Framework v10.x (10.4/10.5). DbContext, repository, migration, eager/lazy loading, and advanced topics.

Trigger
  • "ABP EF Core"
  • "ABP DbContext"
  • "ABP migration"
  • "ABP repository EF"
  • "ABP ConfigureByConvention"
  • "ABP WithDetails"
  • "ABP DbSet"
  • "ABP entity mapping"
Installation
abp add-package Volo.Abp.EntityFrameworkCore
Creating a DbContext
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;

namespace MyCompany.MyProject
{
    public class MyDbContext : AbpDbContext<MyDbContext>
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Author> Authors { get; set; }

        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options) { }
    }
}
Entity Mapping (Fluent API)
protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<Book>(b =>
    {
        b.ToTable("Books");
        b.ConfigureByConvention();  // REQUIRED for base properties
        b.Property(x => x.Name).IsRequired().HasMaxLength(128);
        b.HasIndex(x => x.Name);
    });
}

ConfigureByConvention() must always be called — it automatically configures base class properties (Id, CreationTime, etc.).

DbContext Registration
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
public class MyModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<MyDbContext>(options =>
        {
            options.AddDefaultRepositories();  // Automatic repository for AggregateRoots
            // options.AddDefaultRepositories(includeAllEntities: true);  // For all entities
        });
    }
}
DBMS Configuration
Choosing a DBMS with the CLI
abp new Acme.BookStore -dbms PostgreSQL
abp new Acme.BookStore -dbms MySQL
abp new Acme.BookStore -dbms SQLite
abp new Acme.BookStore -dbms Oracle
Manually Changing the DBMS

PostgreSQL:

# 1. Remove the Volo.Abp.EntityFrameworkCore.SqlServer package
# 2. Add the Volo.Abp.EntityFrameworkCore.PostgreSql package
// Change the module dependency
[DependsOn(typeof(AbpEntityFrameworkCorePostgreSqlModule))]  // instead of SqlServer

// Use UseNpgsql()
Configure<AbpDbContextOptions>(options => options.UseNpgsql());

// Also use UseNpgsql() in the DbContextFactory

// Enable legacy timestamp behavior (Npgsql 6.0+)
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);

MySQL (Pomelo):

Configure<AbpDbContextOptions>(options =>
{
    options.Configure(ctx =>
    {
        if (ctx.ExistingConnection != null)
            ctx.DbContextOptions.UseMySql(ctx.ExistingConnection);
        else
            ctx.DbContextOptions.UseMySql(ctx.ConnectionString);
    });
});

// Set the DBMS provider for modules like OpenIddict (default auth server in ABP v6.0+: OpenIddict)
builder.ConfigureOpenIddict(options =>
{
    options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
});

SQLite:

Configure<AbpDbContextOptions>(options => options.UseSqlite());
Supported DBMSs

| DBMS | Package | |---|---| | SQL Server | Volo.Abp.EntityFrameworkCore.SqlServer | | PostgreSQL | Volo.Abp.EntityFrameworkCore.PostgreSql | | MySQL | Volo.Abp.EntityFrameworkCore.MySQL | | SQLite | Volo.Abp.EntityFrameworkCore.Sqlite | | Oracle | Volo.Abp.EntityFrameworkCore.Oracle |

Choosing a Connection String
[ConnectionStringName("MySecondConnString")]
public class MyDbContext : AbpDbContext<MyDbContext> { }

If not specified, the Default connection string is used.

Using the Default Repository
public class BookManager : DomainService
{
    private readonly IRepository<Book, Guid> _bookRepository;

    public BookManager(IRepository<Book, Guid> bookRepository)
    {
        _bookRepository = bookRepository;
    }

    public async Task<Book> CreateBookAsync(string name, BookType type)
    {
        var book = new Book(GuidGenerator.Create(), name, type);
        await _bookRepository.InsertAsync(book);
        return book;
    }
}
Custom Repository
// Interface (Domain layer)
public interface IBookRepository : IRepository<Book, Guid>
{
    Task DeleteBooksByType(BookType type);
}

// Implementation (EF Core layer)
public class BookRepository : EfCoreRepository<BookStoreDbContext, Book, Guid>, IBookRepository
{
    public BookRepository(IDbContextProvider<BookStoreDbContext> dbContextProvider)
        : base(dbContextProvider) { }

    public async Task DeleteBooksByType(BookType type)
    {
        var dbContext = await GetDbContextAsync();
        await dbContext.Database.ExecuteSqlRawAsync(
            $"DELETE FROM Books WHERE Type = {(int)type}"
        );
    }
}
Overriding the Default Repository
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
    options.AddDefaultRepositories();
    options.AddRepository<Book, BookRepository>();  // BookRepository is used instead of IRepository<Book, Guid>
});
Eager Loading (WithDetails)
// Repository.WithDetailsAsync
var queryable = await _orderRepository.WithDetailsAsync(x => x.Lines);
var orders = await AsyncExecuter.ToListAsync(queryable);

// DefaultWithDetailsFunc configuration
Configure<AbpEntityOptions>(options =>
{
    options.Entity<Order>(orderOptions =>
    {
        orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Lines);
    });
});

// It can then be used without a parameter
var queryable = await _orderRepository.WithDetailsAsync();
includeDetails on Get/Find Methods
var order = await _orderRepository.GetAsync(id);  // includeDetails: true (default)
var order = await _orderRepository.GetAsync(id, includeDetails: false);
var orders = await _orderRepository.GetListAsync(includeDetails: true);
Explicit Loading
var order = await _orderRepository.GetAsync(id, includeDetails: false);
await _orderRepository.EnsureCollectionLoadedAsync(order, x => x.Lines);
// order.Lines is now populated
Lazy Loading
// 1. Install the Microsoft.EntityFrameworkCore.Proxies package
// 2. Configuration
Configure<AbpDbContextOptions>(options =>
{
    options.PreConfigure<MyDbContext>(opts =>
    {
        opts.DbContextOptions.UseLazyLoadingProxies();
    });
    options.UseSqlServer();
});

// 3. Make navigation properties virtual
public virtual ICollection<OrderLine> Lines { get; set; }
public virtual Order Order { get; set; }
Read-Only Repositories
// No-Tracking is applied automatically
public class MyService : ApplicationService
{
    private readonly IReadOnlyRepository<Book, Guid> _bookRepository;
    
    // When tracking is needed
    var query = (await _bookRepository.GetQueryableAsync()).AsTracking();
}
Object Extension Manager (Extra Properties)
ObjectExtensionManager.Instance
    .MapEfCoreProperty<IdentityRole, string>(
        "Title",
        (entityBuilder, propertyBuilder) =>
        {
            propertyBuilder.HasMaxLength(64);
        }
    );
MapEfCoreProperty must be called before the DbContext is used. In startup templates, the EfCoreEntityExtensionMappings class is the safe spot.
Split Queries
Configure<AbpDbContextOptions>(options =>
{
    options.UseSqlServer(optionsBuilder =>
    {
        optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery);
    });
});
EF Core with Multi-Tenancy
[IgnoreMultiTenancy]  // Always use the host connection string
public class TenantManagementDbContext : AbpDbContext<TenantManagementDbContext> { }
Default Repository Base Class
public class MyRepositoryBase<TEntity> : EfCoreRepository<BookStoreDbContext, TEntity>
    where TEntity : class, IEntity
{
    public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
        : base(dbContextProvider) { }
}

// Registration
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
    options.SetDefaultRepositoryClasses(
        typeof(MyRepositoryBase<,>),
        typeof(MyRepositoryBase<>)
    );
});
Best Practices
  1. Always call ConfigureByConvention() — for base property mapping
  2. Prefer the Fluent API — over data annotations
  3. Keep the domain layer isolated from EF Core — use IAsyncQueryableExecuter
  4. Do eager loading with WithDetailsAsync — to avoid the N+1 problem
  5. Use IReadOnlyRepository for read-only queries — No-Tracking is automatic
  6. Define custom repositories in the EF Core layer — only the interface in the domain layer
  7. Manage migrations in the EF Core projectAdd-Migration, Update-Database

Migrations
# Create a migration
dotnet ef migrations add InitialCreate --project Acme.BookStore.EntityFrameworkCore --startup-project Acme.BookStore.DbMigrator

# Apply the migration
dotnet ef database update --project Acme.BookStore.EntityFrameworkCore --startup-project Acme.BookStore.DbMigrator

# or run the DbMigrator
dotnet run --project Acme.BookStore.DbMigrator
Multiple DbContext Migrations
// A separate migration folder for each DbContext
builder.Entity<Book>(b => { ... });  // BookStoreDbContext

// Migration design
public class BookStoreDbContextModelSnapshot : ModelSnapshot { }
Seed Data via Migration
protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.InsertData(
        table: "Books",
        columns: new[] { "Id", "Name", "Price" },
        values: new object[] { Guid.NewGuid(), "1984", 29.99m }
    );
}

ReplaceDbContext Pattern

Combining multiple DbContexts into a single DbContext:

// Define an interface
public interface IBookStoreDbContext : IEfCoreDbContext
{
    DbSet<Book> Books { get; }
}

// Have the DbContext implement the interface
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>, IBookStoreDbContext
{
    public DbSet<Book> Books { get; set; }
}

// Register the default repositories with the interface
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
    options.AddDefaultRepositories<IBookStoreDbContext>();
});

// Another DbContext can replace this interface
[ReplaceDbContext(typeof(IBookStoreDbContext))]
public class UnifiedDbContext : AbpDbContext<UnifiedDbContext>, IBookStoreDbContext
{
    public DbSet<Book> Books { get; set; }
}

Bulk Operations Customization
public class MyCustomEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency
{
    public async Task InsertManyAsync<TDbContext, TEntity>(
        IEfCoreRepository<TEntity> repository,
        IEnumerable<TEntity> entities,
        bool autoSave,
        CancellationToken cancellationToken)
    {
        // Custom bulk insert logic (e.g. EFCore.BulkExtensions)
    }

    public async Task UpdateManyAsync<TDbContext, TEntity>(...) { }
    public async Task DeleteManyAsync<TDbContext, TEntity>(...) { }
}

What's New in v10.5
  • MySQL ResourcePermissionGrant index length fix (v10.5+): for MySQL only, ABP shortened the ResourceName and ResourceKey max lengths of the Permission Management module's ResourcePermissionGrant entity to stay within MySQL's utf8mb4 index key limit. Other providers are unchanged. When creating a fresh MySQL solution or generating new migrations after upgrading, regenerate/review the affected migrations; if you have a custom migration touching ResourcePermissionGrant, align its column lengths with the updated model.
Related
  • [DDD](../abp-ddd/SKILL.md) — entity, aggregate, repository pattern
  • [MongoDB](../abp-mongodb/SKILL.md) — MongoDB alternative
  • [Dependency Rules](../abp-dependency-rules/SKILL.md) — repository interface in Domain, impl in the Data layer
  • [Multi-Tenancy](../abp-multitenancy/SKILL.md) — IgnoreMultiTenancy, tenant connection string
  • [Development Flow](../abp-development-flow/SKILL.md) — migration and DbMigrator flow
  • ABP Docs: https://abp.io/docs/latest/framework/data/entity-framework-core
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。