将 IdentityServer4 与自定义配置 DBContext 一起使用
Posted
技术标签:
【中文标题】将 IdentityServer4 与自定义配置 DBContext 一起使用【英文标题】:using IdentityServer4 with custom Configration DBContext 【发布时间】:2017-11-20 22:36:46 【问题描述】:我创建了一个自定义的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 的正确方法吗?以及如何解决此问题并完成我的迁移工作?
【问题讨论】:
【参考方案1】:您无需创建自定义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。
或
尝试激活
DL.STS.Data.ConfigurationStore.EFCore.AppConfigurationDbContext
时无法解析类型Microsoft.EntityFrameworkCore.DbContextOptions<IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext>
的服务。
我不认为,目前,有办法解决它。
还有其他方法吗?
事实证明这实际上很容易。似乎您不能从 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;
然后在您的网络项目上Startup.cs
:
public void ConfigureServices(IServiceCollection services)
...
string connectionString = _configuration.GetConnectionString("appDbConnection");
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddEFConfigurationStore(connectionString)
...;
...
当你使用PM> Add-Migration AddConfigurationTables -Context ConfigurationDbContext
时,默认项目是那个单独的库/程序集:
【讨论】:
我得到“找不到名为 'ConfigurationDbContext' 的 DbContext”。请检查我的问题***.com/questions/61263843/… 这个答案太棒了,正是我所需要的。我有一个 n 层项目,其中上下文和迁移与 Web 项目分开(完全如此处所述)。在遵循这个答案之后,唯一要添加的是我运行以生成和应用迁移的确切脚本。从 API 项目目录:dotnet ef migrations add AddConfigurationTables -s ../<Api Project Folder> --context ConfigurationDbContext -o Migrations\IdentityServer4
dotnet ef database update AddConfigurationTables -s ../<Api Project Folder> --context ConfigurationDbContext
【参考方案2】:
在最近的版本中,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 =>
【讨论】:
【参考方案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>();
它就像一个魅力。 免责声明:这不是我的想法。我只是不记得它的来源。
【讨论】:
我这样做了,但在迁移过程中出现此错误:在“IdentityConfigurationDbContext”上找不到无参数构造函数。将无参数构造函数添加到“IdentityConfigurationDbContext”或在与“IdentityConfigurationDbContext”相同的程序集中添加“IDbContextFactory添加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());
【讨论】:
哦,是的,你应该有一个IDbContextFactory<CustomConfigurationDbContext>
的实现
您是如何在 Startup.cs 中注册 CustomConfigurationDbContext 的?我收到此错误 System.InvalidOperationException: 'No service for type CustomConfigurationDbContext' has been registered。'
检查我的答案 above 这是我在启动时添加的唯一内容【参考方案5】:
我认为最简单的方法是使用 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);
【讨论】:
【参考方案6】:我使用了您的解决方案,稍作改动。我附在代码下面。 在您的测试方法 OnModelCreating 中,您声明了两个方法
...
modelBuilder.ConfigureClientContext(configurationStoreOptions);
modelBuilder.ConfigureResourcesContext(configurationStoreOptions);
modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions); // need to add
指的是PersistedGrants和DeviceFlowCodes,这很好,但你需要添加
ConfigurePersistedGrantContext also.
here too there is info?
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;
.....
【讨论】:
【参考方案7】:我用 IdentityServer4.EntityFramework (4.1.2) 的最新版本制作了这个:
我创建了一个名为ApplicationConfigurationDbContext
的类继承自ConfigurationDbContext<TContext>
,其中TContext
是我的类
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
类中我注册了我的 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"
通过 .NET CLI here 获得有关 Entity Framework Core 工具的更多信息。
【讨论】:
以上是关于将 IdentityServer4 与自定义配置 DBContext 一起使用的主要内容,如果未能解决你的问题,请参考以下文章