将 IdentityServer4 与自定义配置 DBContext 结合使用
using IdentityServer4 with custom Configration DBContext
我创建了一个自定义的 IConfigurationDbContext
以便将 IDS4 与 Oracle 结合使用。
public class IdentityConfigurationDbContext : DbContext, IConfigurationDbContext {
private readonly ConfigurationStoreOptions storeOptions;
public IdentityConfigurationDbContext(DbContextOptions<IdentityServerDbContext> options)
: base(options) {
}
public IdentityConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options, ConfigurationStoreOptions storeOptions)
: base(options) {
this.storeOptions = storeOptions ?? throw new ArgumentNullException(nameof(storeOptions));
}
public DbSet<Client> Clients { get; set; }
public DbSet<IdentityResource> IdentityResources { get; set; }
public DbSet<ApiResource> ApiResources { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.ConfigureClientContext(storeOptions);
modelBuilder.ConfigureResourcesContext(storeOptions);
base.OnModelCreating(modelBuilder);
}
}
在配置服务中:
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddAspNetIdentity<ApplicationUser>();
我也有我的自定义 IClientStore
,它像这样添加到容器中:
services.AddScoped<IClientStore, ClientStore>();
当我 运行 IdentityConfigurationDbContext
迁移时,出现此错误:
System.InvalidOperationException: No database provider has been configured for this DbContext.
我试过这样做:
services.AddDbContext<IdentityConfigurationDbContext>(builder => builder.UseOracle(connectionString, options => {
options.MigrationsAssembly(migrationsAssembly);
options.MigrationsHistoryTable("EF_MIGRATION_HISTORY");
}));
这是在 IDS4 中使用自定义 dbcontext 的正确方法吗?以及如何解决此问题并完成迁移工作?
我尝试了一种不同的方法。我没有实现 IConfigurationDbContext
,而是继承自 IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext
public class CustomConfigurationDbContext : ConfigurationDbContext
{
public CustomConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options,
ConfigurationStoreOptions storeOptions)
: base(options, storeOptions)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
//...
base.OnConfiguring(optionsBuilder);
}
}
}
并且在 startup.cs
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddConfigurationStore(
builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(
builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)))
.AddAspNetIdentity<ApplicationUser>();
它就像一个魅力。
免责声明:这不是我的主意。我只是不记得它的来源。
添加 IDbContextFactory
解决了这个问题。
public class IdentityConfigurationDbContextFactory : IDbContextFactory<IdentityConfigurationDbContext> {
public IdentityConfigurationDbContext Create(DbContextFactoryOptions options) {
var optionsBuilder = new DbContextOptionsBuilder<ConfigurationDbContext>();
var config = new ConfigurationBuilder()
.SetBasePath(options.ContentRootPath)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{options.EnvironmentName}.json", true)
.Build();
optionsBuilder.UseOracle(config.GetConnectionString("DefaultConnection"));
return new IdentityConfigurationDbContext(optionsBuilder.Options, new ConfigurationStoreOptions());
}
}
在最近的版本中,Identityserver 框架确实支持配置存储、操作存储的自定义实现。这也适用于迁移
见下文
public class CustomPersistsDbContext : DbContext, IPersistedGrantDbContext
{
}
在 OnModelCreating(ModelBuilder modelBuilder) 中,我必须手动添加关系:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//Optional: The version of .NET Core, used by Ef Core Migration history table
modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
//.. Your custom code
//PersistentDbContext
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{
b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<string>("DeviceCode")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime?>("Expiration")
.IsRequired();
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.HasKey("UserCode");
b.HasIndex("DeviceCode")
.IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<string>("Key")
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<DateTime?>("Expiration");
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50);
b.HasKey("Key");
b.HasIndex("SubjectId", "ClientId", "Type");
b.ToTable("PersistedGrants");
});
}
在服务启动时
.AddOperationalStore<CustomPersistsDbContext>(options =>
您无需创建自定义 ConfigurationDbContext
或事件 IDbContextFactory
即可切换为使用不同的数据库。使用 IdentityServer4.EntityFramework
版本 2.3.2,您可以:
namespace DL.STS.Host
{
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
string connectionString = _configuration.GetConnectionString("appDbConnection");
string migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly
.GetName().Name;
services
.AddIdentityServer()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
// I made up this extension method "UseOracle",
// but this is where you plug your database in
builder.UseOracle(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
...;
...
}
...
}
}
分开Configuration/Operational存储到自己的project/assembly?
如果您想很好地布置您的解决方案并希望将配置存储和操作存储(以及身份用户存储)分离到它们自己的 class library/assembly 中怎么办?
根据文档,您可以使用 -o
指定输出迁移文件夹目标:
dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
但是谁喜欢在迁移时 memorize/type 这么长的路径?那么你可能会想:自定义一个继承自 IdentityServer 的 ConfigurationDbContext
和一个单独的项目如何:
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Options;
using Microsoft.EntityFrameworkCore;
namespace DL.STS.Data.ConfigurationStore.EFCore
{
public class AppConfigurationDbContext : ConfigurationDbContext
{
public AppConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options,
ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
{
}
}
}
常见错误
我认为这就是人们遇到麻烦的地方。当你做 Add-Migration
时,你会遇到:
Unable to create an object of type AppConfigurationDbContext
. For different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728.
或
Unable to resolve service for type Microsoft.EntityFrameworkCore.DbContextOptions<IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext>
while attempting to activate DL.STS.Data.ConfigurationStore.EFCore.AppConfigurationDbContext
.
我认为目前没有办法解决它。
还有其他方法吗?
事实证明这其实很容易。似乎您不能从 IdentityServer 继承自己的 DbContext
。所以摆脱它,并在单独的 library/assembly:
中创建一个扩展方法
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace DL.STS.Data.ConfigurationStore.EFCore.Extensions
{
public static class IdentityServerBuilderExtensions
{
public static IIdentityServerBuilder AddEFConfigurationStore(
this IIdentityServerBuilder builder, string connectionString)
{
string assemblyNamespace = typeof(IdentityServerBuilderExtensions)
.GetTypeInfo()
.Assembly
.GetName()
.Name;
builder.AddConfigurationStore(options =>
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString, optionsBuilder =>
optionsBuilder.MigrationsAssembly(assemblyNamespace)
)
);
return builder;
}
}
}
然后 Startup.cs
在您的网络项目上:
public void ConfigureServices(IServiceCollection services)
{
...
string connectionString = _configuration.GetConnectionString("appDbConnection");
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddEFConfigurationStore(connectionString)
...;
...
}
并且当您执行 PM> Add-Migration AddConfigurationTables -Context ConfigurationDbContext
时,默认项目是单独的 library/assembly:
我认为最简单的方法是使用 ConfigurationDbContext 的参数 T,如下所示。它适用于 net core 3.0
public class ConfigurationDataContext : ConfigurationDbContext<ConfigurationDataContext>
{
public ConfigurationDataContext(DbContextOptions<ConfigurationDataContext> options, ConfigurationStoreOptions storeOptions)
: base(options, storeOptions)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(typeof(MyConfigurationsAssemby).Assembly);
}
}
我使用了你的解决方案,稍作改动。我附在代码下面。
在你的测试方法 OnModelCreating 你声明了两个方法
...
modelBuilder.ConfigureClientContext(configurationStoreOptions);
modelBuilder.ConfigureResourcesContext(configurationStoreOptions);
modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions); // need to add
引用 PersistedGrants 和 DeviceFlowCodes,这很好,但您需要添加
ConfigurePersistedGrantContextalso.
public class MYCustomDbContext : DbContext, IPersistedGrantDbContext, IConfigurationDbContext
public DbSet<PersistedGrant> PersistedGrants { get; set; }
public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
........
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
if (modelBuilder is null)
throw new ArgumentNullException(nameof(modelBuilder));
ConfigurationStoreOptions storeConfigurationOptions = new ConfigurationStoreOptions();
OperationalStoreOptions storeOperationalOptions = new OperationalStoreOptions();
modelBuilder.ConfigureClientContext(configurationStoreOptions);
modelBuilder.ConfigureResourcesContext(configurationStoreOptions);
modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions);
}
Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();
public Task<int> SaveChangesAsync() => base.SaveChangesAsync();
public void ConfigureServices(IServiceCollection services)
{
......
services.AddOperationalStore<MYCustomDbContext>(options => {
// this enables automatic token cleanup.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 3600; }
.....
}
我用 IdentityServer4.EntityFramework (4.1.2) 的最新版本做了这个:
我创建了一个名为 ApplicationConfigurationDbContext
的 class 继承自 ConfigurationDbContext<TContext>
,其中 TContext
是我的 class
public class ApplicationConfigurationDbContext : ConfigurationDbContext<ApplicationConfigurationDbContext>
{
public ApplicationConfigurationDbContext(DbContextOptions<ApplicationConfigurationDbContext> options, ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
{
}
// My own entities...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// My own configurations...
}
}
然后在 Startup
class 中我注册了我的 DbContext 最后在调用 AddIdentityServer() 扩展方法后我链接了 AddConfigurationStore() 也有扩展方法,就是这样!
public class Startup
{
// Hide for brevity
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer(options =>
{
// My options...
})
.AddConfigurationStore<ApplicationConfigurationDbContext>(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddAspNetIdentity<ApplicationUser>();
}
}
当我想添加迁移时,我会这样做:
dotnet ef migrations add "Add_New_Table" --context "ApplicationConfigurationDbContext" --startup-project "My.Start.Project" --project "Target.Project" --output-dir "Migrations/ConfigurationDb"
有关 Entity Framework 核心工具的更多信息,通过 .NET CLI here。
我创建了一个自定义的 IConfigurationDbContext
以便将 IDS4 与 Oracle 结合使用。
public class IdentityConfigurationDbContext : DbContext, IConfigurationDbContext {
private readonly ConfigurationStoreOptions storeOptions;
public IdentityConfigurationDbContext(DbContextOptions<IdentityServerDbContext> options)
: base(options) {
}
public IdentityConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options, ConfigurationStoreOptions storeOptions)
: base(options) {
this.storeOptions = storeOptions ?? throw new ArgumentNullException(nameof(storeOptions));
}
public DbSet<Client> Clients { get; set; }
public DbSet<IdentityResource> IdentityResources { get; set; }
public DbSet<ApiResource> ApiResources { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.ConfigureClientContext(storeOptions);
modelBuilder.ConfigureResourcesContext(storeOptions);
base.OnModelCreating(modelBuilder);
}
}
在配置服务中:
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddAspNetIdentity<ApplicationUser>();
我也有我的自定义 IClientStore
,它像这样添加到容器中:
services.AddScoped<IClientStore, ClientStore>();
当我 运行 IdentityConfigurationDbContext
迁移时,出现此错误:
System.InvalidOperationException: No database provider has been configured for this DbContext.
我试过这样做:
services.AddDbContext<IdentityConfigurationDbContext>(builder => builder.UseOracle(connectionString, options => {
options.MigrationsAssembly(migrationsAssembly);
options.MigrationsHistoryTable("EF_MIGRATION_HISTORY");
}));
这是在 IDS4 中使用自定义 dbcontext 的正确方法吗?以及如何解决此问题并完成迁移工作?
我尝试了一种不同的方法。我没有实现 IConfigurationDbContext
,而是继承自 IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext
public class CustomConfigurationDbContext : ConfigurationDbContext
{
public CustomConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options,
ConfigurationStoreOptions storeOptions)
: base(options, storeOptions)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
//...
base.OnConfiguring(optionsBuilder);
}
}
}
并且在 startup.cs
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddConfigurationStore(
builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(
builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)))
.AddAspNetIdentity<ApplicationUser>();
它就像一个魅力。 免责声明:这不是我的主意。我只是不记得它的来源。
添加 IDbContextFactory
解决了这个问题。
public class IdentityConfigurationDbContextFactory : IDbContextFactory<IdentityConfigurationDbContext> {
public IdentityConfigurationDbContext Create(DbContextFactoryOptions options) {
var optionsBuilder = new DbContextOptionsBuilder<ConfigurationDbContext>();
var config = new ConfigurationBuilder()
.SetBasePath(options.ContentRootPath)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{options.EnvironmentName}.json", true)
.Build();
optionsBuilder.UseOracle(config.GetConnectionString("DefaultConnection"));
return new IdentityConfigurationDbContext(optionsBuilder.Options, new ConfigurationStoreOptions());
}
}
在最近的版本中,Identityserver 框架确实支持配置存储、操作存储的自定义实现。这也适用于迁移
见下文
public class CustomPersistsDbContext : DbContext, IPersistedGrantDbContext
{
}
在 OnModelCreating(ModelBuilder modelBuilder) 中,我必须手动添加关系:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//Optional: The version of .NET Core, used by Ef Core Migration history table
modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
//.. Your custom code
//PersistentDbContext
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{
b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<string>("DeviceCode")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime?>("Expiration")
.IsRequired();
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.HasKey("UserCode");
b.HasIndex("DeviceCode")
.IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<string>("Key")
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<DateTime?>("Expiration");
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50);
b.HasKey("Key");
b.HasIndex("SubjectId", "ClientId", "Type");
b.ToTable("PersistedGrants");
});
}
在服务启动时
.AddOperationalStore<CustomPersistsDbContext>(options =>
您无需创建自定义 ConfigurationDbContext
或事件 IDbContextFactory
即可切换为使用不同的数据库。使用 IdentityServer4.EntityFramework
版本 2.3.2,您可以:
namespace DL.STS.Host
{
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
string connectionString = _configuration.GetConnectionString("appDbConnection");
string migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly
.GetName().Name;
services
.AddIdentityServer()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
// I made up this extension method "UseOracle",
// but this is where you plug your database in
builder.UseOracle(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
...;
...
}
...
}
}
分开Configuration/Operational存储到自己的project/assembly?
如果您想很好地布置您的解决方案并希望将配置存储和操作存储(以及身份用户存储)分离到它们自己的 class library/assembly 中怎么办?
根据文档,您可以使用 -o
指定输出迁移文件夹目标:
dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
但是谁喜欢在迁移时 memorize/type 这么长的路径?那么你可能会想:自定义一个继承自 IdentityServer 的 ConfigurationDbContext
和一个单独的项目如何:
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Options;
using Microsoft.EntityFrameworkCore;
namespace DL.STS.Data.ConfigurationStore.EFCore
{
public class AppConfigurationDbContext : ConfigurationDbContext
{
public AppConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options,
ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
{
}
}
}
常见错误
我认为这就是人们遇到麻烦的地方。当你做 Add-Migration
时,你会遇到:
Unable to create an object of type
AppConfigurationDbContext
. For different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728.
或
Unable to resolve service for type
Microsoft.EntityFrameworkCore.DbContextOptions<IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext>
while attempting to activateDL.STS.Data.ConfigurationStore.EFCore.AppConfigurationDbContext
.
我认为目前没有办法解决它。
还有其他方法吗?
事实证明这其实很容易。似乎您不能从 IdentityServer 继承自己的 DbContext
。所以摆脱它,并在单独的 library/assembly:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace DL.STS.Data.ConfigurationStore.EFCore.Extensions
{
public static class IdentityServerBuilderExtensions
{
public static IIdentityServerBuilder AddEFConfigurationStore(
this IIdentityServerBuilder builder, string connectionString)
{
string assemblyNamespace = typeof(IdentityServerBuilderExtensions)
.GetTypeInfo()
.Assembly
.GetName()
.Name;
builder.AddConfigurationStore(options =>
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString, optionsBuilder =>
optionsBuilder.MigrationsAssembly(assemblyNamespace)
)
);
return builder;
}
}
}
然后 Startup.cs
在您的网络项目上:
public void ConfigureServices(IServiceCollection services)
{
...
string connectionString = _configuration.GetConnectionString("appDbConnection");
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddEFConfigurationStore(connectionString)
...;
...
}
并且当您执行 PM> Add-Migration AddConfigurationTables -Context ConfigurationDbContext
时,默认项目是单独的 library/assembly:
我认为最简单的方法是使用 ConfigurationDbContext 的参数 T,如下所示。它适用于 net core 3.0
public class ConfigurationDataContext : ConfigurationDbContext<ConfigurationDataContext>
{
public ConfigurationDataContext(DbContextOptions<ConfigurationDataContext> options, ConfigurationStoreOptions storeOptions)
: base(options, storeOptions)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(typeof(MyConfigurationsAssemby).Assembly);
}
}
我使用了你的解决方案,稍作改动。我附在代码下面。 在你的测试方法 OnModelCreating 你声明了两个方法
...
modelBuilder.ConfigureClientContext(configurationStoreOptions);
modelBuilder.ConfigureResourcesContext(configurationStoreOptions);
modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions); // need to add
引用 PersistedGrants 和 DeviceFlowCodes,这很好,但您需要添加
ConfigurePersistedGrantContextalso.
public class MYCustomDbContext : DbContext, IPersistedGrantDbContext, IConfigurationDbContext
public DbSet<PersistedGrant> PersistedGrants { get; set; }
public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
........
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
if (modelBuilder is null)
throw new ArgumentNullException(nameof(modelBuilder));
ConfigurationStoreOptions storeConfigurationOptions = new ConfigurationStoreOptions();
OperationalStoreOptions storeOperationalOptions = new OperationalStoreOptions();
modelBuilder.ConfigureClientContext(configurationStoreOptions);
modelBuilder.ConfigureResourcesContext(configurationStoreOptions);
modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions);
}
Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();
public Task<int> SaveChangesAsync() => base.SaveChangesAsync();
public void ConfigureServices(IServiceCollection services)
{
......
services.AddOperationalStore<MYCustomDbContext>(options => {
// this enables automatic token cleanup.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 3600; }
.....
}
我用 IdentityServer4.EntityFramework (4.1.2) 的最新版本做了这个:
我创建了一个名为 ApplicationConfigurationDbContext
的 class 继承自 ConfigurationDbContext<TContext>
,其中 TContext
是我的 class
public class ApplicationConfigurationDbContext : ConfigurationDbContext<ApplicationConfigurationDbContext>
{
public ApplicationConfigurationDbContext(DbContextOptions<ApplicationConfigurationDbContext> options, ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
{
}
// My own entities...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// My own configurations...
}
}
然后在 Startup
class 中我注册了我的 DbContext 最后在调用 AddIdentityServer() 扩展方法后我链接了 AddConfigurationStore() 也有扩展方法,就是这样!
public class Startup
{
// Hide for brevity
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer(options =>
{
// My options...
})
.AddConfigurationStore<ApplicationConfigurationDbContext>(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddAspNetIdentity<ApplicationUser>();
}
}
当我想添加迁移时,我会这样做:
dotnet ef migrations add "Add_New_Table" --context "ApplicationConfigurationDbContext" --startup-project "My.Start.Project" --project "Target.Project" --output-dir "Migrations/ConfigurationDb"
有关 Entity Framework 核心工具的更多信息,通过 .NET CLI here。