Entity Framework Core Integration Best Practices
See Entity Framework Core Integration document for the basics of the EF Core integration.
- Do define a separated 
DbContextinterface and class for each module. - Do not rely on lazy loading on the application development.
 - Do not enable lazy loading for the 
DbContext. 
DbContext Interface
- Do define an interface for the 
DbContextthat inherits fromIEfCoreDbContext. - Do add a 
ConnectionStringNameattribute to theDbContextinterface. - Do add 
DbSet<TEntity>properties to theDbContextinterface for only aggregate roots. Example: 
[ConnectionStringName("AbpIdentity")]
public interface IIdentityDbContext : IEfCoreDbContext
{
    DbSet<IdentityUser> Users { get; }
    DbSet<IdentityRole> Roles { get; }
}
- Do not define 
set;for the properties in this interface. 
DbContext class
- Do inherit the 
DbContextfrom theAbpDbContext<TDbContext>class. - Do add a 
ConnectionStringNameattribute to theDbContextclass. - Do implement the corresponding 
interfacefor theDbContextclass. Example: 
[ConnectionStringName("AbpIdentity")]
public class IdentityDbContext : AbpDbContext<IdentityDbContext>, IIdentityDbContext
{
    public DbSet<IdentityUser> Users { get; set; }
    public DbSet<IdentityRole> Roles { get; set; }
    public IdentityDbContext(DbContextOptions<IdentityDbContext> options)
        : base(options)
    {
    }
    //code omitted for brevity
}
Table Prefix and Schema
- Do add static 
TablePrefixandSchemaproperties to theDbContextclass. Set default value from a constant. Example: 
public static string TablePrefix { get; set; } = AbpIdentityConsts.DefaultDbTablePrefix;
public static string Schema { get; set; } = AbpIdentityConsts.DefaultDbSchema;
- Do always use a short 
TablePrefixvalue for a module to create unique table names in a shared database.Abptable prefix is reserved for ABP core modules. - Do set 
Schematonullas default. 
Model Mapping
- Do explicitly configure all entities by overriding the 
OnModelCreatingmethod of theDbContext. Example: 
protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.ConfigureIdentity(options =>
    {
        options.TablePrefix = TablePrefix;
        options.Schema = Schema;
    });
}
- Do not configure model directly in the  
OnModelCreatingmethod. Instead, create an extension method forModelBuilder. Use ConfigureModuleName as the method name. Example: 
public static class IdentityDbContextModelBuilderExtensions
{
    public static void ConfigureIdentity(
        [NotNull] this ModelBuilder builder,
        Action<IdentityModelBuilderConfigurationOptions> optionsAction = null)
    {
        Check.NotNull(builder, nameof(builder));
        var options = new IdentityModelBuilderConfigurationOptions();
        optionsAction?.Invoke(options);
        builder.Entity<IdentityUser>(b =>
        {
            b.ToTable(options.TablePrefix + "Users", options.Schema);
            b.ConfigureByConvention();
            //code omitted for brevity
        });
        builder.Entity<IdentityUserClaim>(b =>
        {
            b.ToTable(options.TablePrefix + "UserClaims", options.Schema);
            b.ConfigureByConvention();
            //code omitted for brevity
        });
        
        //code omitted for brevity
    }
}
- Do call 
b.ConfigureByConvention();for each entity mapping (as shown above). - Do create a configuration options class by inheriting from the 
AbpModelBuilderConfigurationOptions. Example: 
public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{
    public IdentityModelBuilderConfigurationOptions()
        : base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema)
    {
    }
}
Repository Implementation
- Do inherit the repository from the 
EfCoreRepository<TDbContext, TEntity, TKey>class and implement the corresponding repository interface. Example: 
public class EfCoreIdentityUserRepository
    : EfCoreRepository<IIdentityDbContext, IdentityUser, Guid>, IIdentityUserRepository
{
    public EfCoreIdentityUserRepository(
        IDbContextProvider<IIdentityDbContext> dbContextProvider)
        : base(dbContextProvider)
    {
    }
}
- Do use the 
DbContextinterface as the generic parameter, not the class. - Do pass the 
cancellationTokento EF Core using theGetCancellationTokenhelper method. Example: 
public virtual async Task<IdentityUser> FindByNormalizedUserNameAsync(
    string normalizedUserName, 
    bool includeDetails = true,
    CancellationToken cancellationToken = default)
{
    return await (await GetDbSetAsync())
        .IncludeDetails(includeDetails)
        .FirstOrDefaultAsync(
            u => u.NormalizedUserName == normalizedUserName,
            GetCancellationToken(cancellationToken)
        );
}
GetCancellationToken fallbacks to the ICancellationTokenProvider.Token to obtain the cancellation token if it is not provided by the caller code.
- Do create a 
IncludeDetailsextension method for theIQueryable<TEntity>for each aggregate root which has sub collections. Example: 
public static IQueryable<IdentityUser> IncludeDetails(
    this IQueryable<IdentityUser> queryable,
    bool include = true)
{
    if (!include)
    {
        return queryable;
    }
    return queryable
        .Include(x => x.Roles)
        .Include(x => x.Logins)
        .Include(x => x.Claims)
        .Include(x => x.Tokens);
}
- Do use the 
IncludeDetailsextension method in the repository methods just like used in the example code above (seeFindByNormalizedUserNameAsync). 
- Do override 
WithDetailsmethod of the repository for aggregates root which have sub collections. Example: 
public override async Task<IQueryable<IdentityUser>> WithDetailsAsync()
{
    // Uses the extension method defined above
    return (await GetQueryableAsync()).IncludeDetails();
}
Module Class
- Do define a module class for the Entity Framework Core integration package.
 - Do add 
DbContextto theIServiceCollectionusing theAddAbpDbContext<TDbContext>method. - Do add implemented repositories to the options for the 
AddAbpDbContext<TDbContext>method. Example: 
[DependsOn(
    typeof(AbpIdentityDomainModule),
    typeof(AbpEntityFrameworkCoreModule)
    )]
public class AbpIdentityEntityFrameworkCoreModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<IdentityDbContext>(options =>
        {
            options.AddRepository<IdentityUser, EfCoreIdentityUserRepository>();
            options.AddRepository<IdentityRole, EfCoreIdentityRoleRepository>();
        });
    }
}