From ABP Sensei
Guides ABP Framework v10.x EF Core integration: DbContext setup, entity mapping with ConfigureByConvention, repository registration, and DBMS selection (PostgreSQL/MySQL/SQLite/Oracle).
How this skill is triggered — by the user, by Claude, or both
Slash command
/abp-sensei:abp-efcoreThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A guide to EF Core integration in ABP Framework v10.x (10.4/10.5). DbContext, repository, migration, eager/lazy loading, and advanced topics.
A guide to EF Core integration in ABP Framework v10.x (10.4/10.5). DbContext, repository, migration, eager/lazy loading, and advanced topics.
abp add-package Volo.Abp.EntityFrameworkCore
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) { }
}
}
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.).
[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
});
}
}
abp new Acme.BookStore -dbms PostgreSQL
abp new Acme.BookStore -dbms MySQL
abp new Acme.BookStore -dbms SQLite
abp new Acme.BookStore -dbms Oracle
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());
| 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 |
[ConnectionStringName("MySecondConnString")]
public class MyDbContext : AbpDbContext<MyDbContext> { }
If not specified, the Default connection string is used.
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;
}
}
// 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}"
);
}
}
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
options.AddDefaultRepositories();
options.AddRepository<Book, BookRepository>(); // BookRepository is used instead of IRepository<Book, Guid>
});
// 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();
var order = await _orderRepository.GetAsync(id); // includeDetails: true (default)
var order = await _orderRepository.GetAsync(id, includeDetails: false);
var orders = await _orderRepository.GetListAsync(includeDetails: true);
var order = await _orderRepository.GetAsync(id, includeDetails: false);
await _orderRepository.EnsureCollectionLoadedAsync(order, x => x.Lines);
// order.Lines is now populated
// 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; }
// 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();
}
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityRole, string>(
"Title",
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(64);
}
);
MapEfCorePropertymust be called before the DbContext is used. In startup templates, theEfCoreEntityExtensionMappingsclass is the safe spot.
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer(optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery);
});
});
[IgnoreMultiTenancy] // Always use the host connection string
public class TenantManagementDbContext : AbpDbContext<TenantManagementDbContext> { }
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<>)
);
});
ConfigureByConvention() — for base property mappingIAsyncQueryableExecuterWithDetailsAsync — to avoid the N+1 problemIReadOnlyRepository for read-only queries — No-Tracking is automaticAdd-Migration, Update-Database# 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
// A separate migration folder for each DbContext
builder.Entity<Book>(b => { ... }); // BookStoreDbContext
// Migration design
public class BookStoreDbContextModelSnapshot : ModelSnapshot { }
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "Books",
columns: new[] { "Id", "Name", "Price" },
values: new object[] { Guid.NewGuid(), "1984", 29.99m }
);
}
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; }
}
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>(...) { }
}
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.Guides ABP Framework v10.x MongoDB integration: MongoDbContext, collection mapping, indexes, transactions, and repository patterns for .NET projects.
Entity Framework Core patterns for ASP.NET Core: DbContext design, Fluent API entity configuration, relation mapping, N+1 prevention, transactions, and parameterized raw SQL.
Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, interceptors, compiled queries, and connection resiliency. Use when setting up EF Core in a new project, optimizing query performance, managing database migrations, integrating EF Core with .NET Aspire, or debugging change tracking issues.
npx claudepluginhub burakdmir/abp-skills --plugin abp-sensei