Agent skill

abp-ef-core

ABP Entity Framework Core - DbContext, entity configuration, EfCoreRepository implementation, migrations (dotnet ef migrations add), data seeding. Use when working in EntityFrameworkCore projects, adding migrations, or implementing EF Core repositories.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/abp-ef-core

SKILL.md

ABP Entity Framework Core

Docs: https://abp.io/docs/latest/framework/data/entity-framework-core

Never Do

Don't Do Instead
Skip b.ConfigureByConvention() Always call it first in entity config
AddDefaultRepositories(includeAllEntities: true) Use AddDefaultRepositories() only for aggregate roots
Inject DbContext in application/domain services Use IRepository<T> or custom repository interface
Use DbContext directly outside the EF Core project Access via GetDbContextAsync() inside repository only

DbContext Configuration

csharp
[ConnectionStringName("Default")]
public class MyProjectDbContext : AbpDbContext<MyProjectDbContext>
{
    public DbSet<Book> Books { get; set; }
    public DbSet<Author> Authors { get; set; }

    public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        // Configure all entities
        builder.ConfigureMyProject();
    }
}

Entity Configuration

csharp
public static class MyProjectDbContextModelCreatingExtensions
{
    public static void ConfigureMyProject(this ModelBuilder builder)
    {
        Check.NotNull(builder, nameof(builder));

        builder.Entity<Book>(b =>
        {
            b.ToTable(MyProjectConsts.DbTablePrefix + "Books", MyProjectConsts.DbSchema);
            b.ConfigureByConvention(); // ABP conventions (audit, soft-delete, etc.)

            // Property configurations
            b.Property(x => x.Name)
                .IsRequired()
                .HasMaxLength(BookConsts.MaxNameLength);

            b.Property(x => x.Price)
                .HasColumnType("decimal(18,2)");

            // Indexes
            b.HasIndex(x => x.Name);

            // Relationships
            b.HasOne<Author>()
                .WithMany()
                .HasForeignKey(x => x.AuthorId)
                .OnDelete(DeleteBehavior.Restrict);
        });
    }
}

Repository Implementation

csharp
public class BookRepository : EfCoreRepository<MyProjectDbContext, Book, Guid>, IBookRepository
{
    public BookRepository(IDbContextProvider<MyProjectDbContext> dbContextProvider)
        : base(dbContextProvider)
    {
    }

    public async Task<Book> FindByNameAsync(
        string name,
        bool includeDetails = true,
        CancellationToken cancellationToken = default)
    {
        var dbSet = await GetDbSetAsync();

        return await dbSet
            .IncludeDetails(includeDetails)
            .FirstOrDefaultAsync(
                b => b.Name == name,
                GetCancellationToken(cancellationToken));
    }

    public async Task<List<Book>> GetListByAuthorAsync(
        Guid authorId,
        bool includeDetails = false,
        CancellationToken cancellationToken = default)
    {
        var dbSet = await GetDbSetAsync();

        return await dbSet
            .IncludeDetails(includeDetails)
            .Where(b => b.AuthorId == authorId)
            .ToListAsync(GetCancellationToken(cancellationToken));
    }

    public override async Task<IQueryable<Book>> WithDetailsAsync()
    {
        return (await GetQueryableAsync())
            .Include(b => b.Reviews);
    }
}

Extension Method for Include

csharp
public static class BookEfCoreQueryableExtensions
{
    public static IQueryable<Book> IncludeDetails(
        this IQueryable<Book> queryable,
        bool include = true)
    {
        if (!include)
        {
            return queryable;
        }

        return queryable
            .Include(b => b.Reviews);
    }
}

Migration Commands

bash
# Navigate to EF Core project
cd src/MyProject.EntityFrameworkCore

# Add migration
dotnet ef migrations add MigrationName

# Apply migration (choose one):
dotnet run --project ../MyProject.DbMigrator   # Recommended - also seeds data
dotnet ef database update  # EF Core command only

# Remove last migration (if not applied)
dotnet ef migrations remove

# Generate SQL script
dotnet ef migrations script

Note: ABP templates include IDesignTimeDbContextFactory in the EF Core project, so -s (startup project) parameter is not needed.

Module Configuration

csharp
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
public class MyProjectEntityFrameworkCoreModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<MyProjectDbContext>(options =>
        {
            // Add default repositories for aggregate roots only (DDD best practice)
            options.AddDefaultRepositories();
            // ⚠️ Avoid includeAllEntities: true - it creates repositories for child entities,
            // allowing them to be modified without going through the aggregate root,
            // which breaks data consistency
        });

        Configure<AbpDbContextOptions>(options =>
        {
            options.UseSqlServer(); // or UseNpgsql(), UseMySql(), etc.
        });
    }
}

Best Practices

Repositories for Aggregate Roots Only

Don't use includeAllEntities: true in AddDefaultRepositories(). This creates repositories for child entities, allowing direct modification without going through the aggregate root - breaking DDD data consistency rules.

csharp
// ✅ Correct - Only aggregate roots get repositories
options.AddDefaultRepositories();

// ❌ Avoid - Creates repositories for ALL entities including child entities
options.AddDefaultRepositories(includeAllEntities: true);

Always Call ConfigureByConvention

csharp
builder.Entity<MyEntity>(b =>
{
    b.ConfigureByConvention(); // Don't forget this!
    // Other configurations...
});

Use Table Prefix

csharp
public static class MyProjectConsts
{
    public const string DbTablePrefix = "App";
    public const string DbSchema = null; // Or "myschema"
}

Performance Tips

  • Add explicit indexes for frequently queried fields
  • Use AsNoTracking() for read-only queries
  • Avoid N+1 queries with .Include() or specifications
  • ABP handles cancellation automatically; use GetCancellationToken(cancellationToken) only in custom repository methods
  • Consider query splitting for complex queries with multiple collections

Accessing Raw DbContext

csharp
public async Task CustomOperationAsync()
{
    var dbContext = await GetDbContextAsync();

    // Raw SQL
    await dbContext.Database.ExecuteSqlRawAsync(
        "UPDATE Books SET IsPublished = 1 WHERE AuthorId = {0}",
        authorId
    );
}

Data Seeding

csharp
public class MyProjectDataSeedContributor : IDataSeedContributor, ITransientDependency
{
    private readonly IRepository<Book, Guid> _bookRepository;
    private readonly IGuidGenerator _guidGenerator;

    public async Task SeedAsync(DataSeedContext context)
    {
        if (await _bookRepository.GetCountAsync() > 0)
        {
            return;
        }

        await _bookRepository.InsertAsync(
            new Book(_guidGenerator.Create(), "Sample Book", 19.99m, Guid.Empty),
            autoSave: true
        );
    }
}

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results