我创建了一个自定义的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);
}
}
在ConfigureService中:
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的正确方法吗?以及如何解决此问题,并完成我的迁移工作?
答案 0 :(得分:3)
我尝试过不同的方法。我没有实施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>();
它就像一个魅力。 免责声明:这不是我的想法。我只是记不起那个来源。
答案 1 :(得分:1)
添加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());
}
}
答案 2 :(得分:0)
在最近发布的版本中,Identityserver框架确实支持配置存储,操作存储的自定义实现。
见下文例如
public class CustomPersistsDbContext : DbContext, IPersistedGrantDbContext
{
}
在服务启动
上 .AddOperationalStore<CustomPersistsDbContext>(options =>
答案 3 :(得分:0)
您无需创建自定义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));
})
...;
...
}
...
}
}
如果您想很好地布置您的解决方案,并且想将配置存储和操作存储(以及身份用户存储)分离到各自的类库/程序集中,该怎么办?
根据文档,您可以使用-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
但是,谁愿意在迁移时记住/输入这么长的路呢?然后,您可能会想:从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
时,您会遇到:
无法创建类型
AppConfigurationDbContext
的对象。有关设计时支持的不同模式,请参见https://go.microsoft.com/fwlink/?linkid=851728。
或
尝试激活
Microsoft.EntityFrameworkCore.DbContextOptions<IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext>
时无法解析类型为DL.STS.Data.ConfigurationStore.EFCore.AppConfigurationDbContext
的服务。
我不认为,目前有一种解决方法。
事实证明,这实际上很容易。看来您无法拥有从IdentityServer继承的DbContext
。因此,摆脱它,并在该单独的库/程序集中创建扩展方法:
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;
}
}
}
然后在您的Web项目上的Startup.cs
上
public void ConfigureServices(IServiceCollection services)
{
...
string connectionString = _configuration.GetConnectionString("appDbConnection");
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddEFConfigurationStore(connectionString)
...;
...
}
当您使用默认项目是单独的库/程序集进行PM> Add-Migration AddConfigurationTables -Context ConfigurationDbContext
时:
答案 4 :(得分:0)
我认为最简单的方法是使用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);
}
}
答案 5 :(得分:0)
我用您的解决方案做了一点改动。我在代码下方附加。 在测试方法OnModelCreating中,您声明了两个方法
...
modelBuilder.ConfigureClientContext(configurationStoreOptions);
modelBuilder.ConfigureResourcesContext(configurationStoreOptions);
modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions); // need to add
指的是PersistedGrants和DeviceFlowCodes,这很好,但是您需要添加
ConfigurePersistedGrantContext also.
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; }
.....
}