ABP 如何與EFCore整合
阿新 • • 發佈:2020-08-19
- 每個模組定義單獨的DbContext類
- 不要在應用程式開發中使用延遲載入
- 不要為DbContext啟用延遲載入
DbContext Interface
- 介面繼承自IEfCoreDbContext介面
- 新增ConnectionStringName特性到DbContext介面
- 將DbSet
屬性新增到DbContext介面中,注意:僅適用於聚合根
DbContext Class
- DbContext類繼承自 AbpDbContext
類 - 新增ConnectionStringName 特性到DbContext類
- 實現DbContext介面
[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 }
表字首與架構
- 新增靜態屬性 TablePrefix和Schema 到DbContext類,使用常量為其設定一個預設值
public static string TablePrefix { get; set; } = AbpIdentityConsts.DefaultDbTablePrefix;
public static string Schema { get; set; } = AbpIdentityConsts.DefaultDbSchema;
- 要用簡短的TablePrefix值為模組在共享資料庫中建立不重複的表名
- Schema預設賦值為null
模型對映
- 重寫DbContext的OnModelCreating方法顯式配置所有實體
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureIdentity(options =>
{
options.TablePrefix = TablePrefix;
options.Schema = Schema;
});
}
- 不要直接在OnModelCreating方法中配置model ,而是為ModelBuilder定義一個擴充套件方法 ,使用ConfigureModuleName作為方法名稱, 例:
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
}
}
- 為每個Entity對映呼叫 b.ConfigureByConvention()
- 通過繼承 AbpModelBuilderConfigurationOptions 來建立 configuration Options 類. 例如:
public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{
public IdentityModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema)
{
}
}
倉儲實現
- 從EfCoreRepository<TDbContext,TEntity,TKey> 類繼承倉儲並實現相應的倉儲介面 ,例:
public class EfCoreIdentityUserRepository
: EfCoreRepository<IIdentityDbContext, IdentityUser, Guid>, IIdentityUserRepository
{
public EfCoreIdentityUserRepository(
IDbContextProvider<IIdentityDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
- 使用DbContext介面而不是類作為泛型引數
- 使用GetCancellationToken幫助方法將cancellationToken傳遞給EF Core 例
public virtual async Task<IdentityUser> FindByNormalizedUserNameAsync(
string normalizedUserName,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await DbSet
.IncludeDetails(includeDetails)
.FirstOrDefaultAsync(
u => u.NormalizedUserName == normalizedUserName,
GetCancellationToken(cancellationToken)
);
}
如果呼叫者程式碼中未提供取消令牌,則GetCancellationToken會從ICancellationTokenProvider.Token獲取取消令牌
- 為具有子集合的聚合根建立 IQueryable
返回型別的 IncludeDetails 擴充套件方法.
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);
}
- 在倉儲其他方法中使用 IncludeDetails 擴充套件方法, 就像上面的示例程式碼一樣(參閱 FindByNormalizedUserNameAsync).
- 覆蓋具有 子集合 的聚合根倉儲中的 WithDetails 方法. 例如:
public override IQueryable<IdentityUser> WithDetails()
{
return GetQueryable().IncludeDetails(); // Uses the extension method defined above
}
模組類
- 為Entity Framework Core整合包定義一個Module類.
- 使用 AddAbpDbContext
方法將 DbContext 新增到 IServiceCollection. - 將已實現的倉儲新增到 AddAbpDbContext
方法的options中. 例如:
[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>();
});
}
}