| name | abp-ef-core |
| description | 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. |
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
[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);
builder.ConfigureMyProject();
}
}
Entity Configuration
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();
b.Property(x => x.Name)
.IsRequired()
.HasMaxLength(BookConsts.MaxNameLength);
b.Property(x => x.Price)
.HasColumnType("decimal(18,2)");
b.HasIndex(x => x.Name);
b.HasOne<Author>()
.WithMany()
.HasForeignKey(x => x.AuthorId)
.OnDelete(DeleteBehavior.Restrict);
});
}
}
Repository Implementation
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
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
cd src/MyProject.EntityFrameworkCore
dotnet ef migrations add MigrationName
dotnet run --project ../MyProject.DbMigrator
dotnet ef database update
dotnet ef migrations remove
dotnet ef migrations script
Note: ABP templates include IDesignTimeDbContextFactory in the EF Core project, so -s (startup project) parameter is not needed.
Module Configuration
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
public class MyProjectEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<MyProjectDbContext>(options =>
{
options.AddDefaultRepositories();
});
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
});
}
}
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.
options.AddDefaultRepositories();
options.AddDefaultRepositories(includeAllEntities: true);
Always Call ConfigureByConvention
builder.Entity<MyEntity>(b =>
{
b.ConfigureByConvention();
});
Use Table Prefix
public static class MyProjectConsts
{
public const string DbTablePrefix = "App";
public const string DbSchema = null;
}
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
public async Task CustomOperationAsync()
{
var dbContext = await GetDbContextAsync();
await dbContext.Database.ExecuteSqlRawAsync(
"UPDATE Books SET IsPublished = 1 WHERE AuthorId = {0}",
authorId
);
}
Data Seeding
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
);
}
}