这个答案有两个部分;首先需要在 IdentityServer 的配置中调整表名称,以便它使用新的表名称生成查询。第二;需要修改实体框架生成的模式,以便它知道为身份框架实体创建不同名称的表。继续阅读...
所以,首先;更改实体框架查询中使用的表名称的能力公开在AddOperationalStore
and AddConfigurationStore
挂起的方法AddIdentityServer
中间件方法。这options
提供给配置方法的委托参数公开表名称,例如:options.{EntityName}.Name = {WhateverTableNameYouWantToUse}
- or options.ApiResource.Name = mytesttable
。您还可以通过调整每个表的基础来覆盖架构Schema
财产。
下面的示例使用反射来更新所有实体以使用前缀为的表名idn_
, so idn_ApiResources
, idn_ApiScopes
etc:
services.AddIdentityServer()
.AddConfigurationStore(options => {
// Loop through and rename each table to 'idn_{tablename}' - E.g. `idn_ApiResources`
foreach(var p in options.GetType().GetProperties()) {
if (p.PropertyType == typeof(IdentityServer4.EntityFramework.Options.TableConfiguration))
{
object o = p.GetGetMethod().Invoke(options, null);
PropertyInfo q = o.GetType().GetProperty("Name");
string tableName = q.GetMethod.Invoke(o, null) as string;
o.GetType().GetProperty("Name").SetMethod.Invoke(o, new object[] { $"idn_{tableName}" });
}
}
// Configure DB Context connection string and migrations assembly where migrations are stored
options.ConfigureDbContext = builder => builder.UseNpgsql(_configuration.GetConnectionString("IDPDataDBConnectionString"),
sql => sql.MigrationsAssembly(typeof(IdentityServer.Data.DbContexts.MyTestDbContext).GetTypeInfo().Assembly.GetName().Name));
}
.AddOperationalStore(options => {
// Copy and paste from AddConfigurationStore logic above.
}
第二部分是修改实体框架从 IdentityServer 实体生成的模式。要实现这一目标,您有两个选择:您可以从 IdentityServer 提供的 DBContext 之一派生;ConfigurationDbContext
or PeristedGrantDbContext
然后覆盖OnModelCreating
方法将每个 IdentityServer 实体重新映射到修改后的表名称,然后创建初始迁移或更新迁移为记录在这里 https://learn.microsoft.com/en-us/ef/core/modeling/relational/tables(流利的Api语法),or您可以从提供的 IdentityServer DBContext 创建初始迁移ConfigurationDbContext
and PersistedGrantDbContext
按照教程添加迁移 https://identityserver4.readthedocs.io/en/release/quickstarts/8_entity_framework.html部分,然后使用文本编辑器对所有表名称以及对创建的迁移文件中这些表名称的引用进行查找和替换。
无论您选择哪种方法,您仍然需要使用dotnet ef migrations ...
命令行语法用于创建初始迁移文件,如下所示添加迁移 https://identityserver4.readthedocs.io/en/release/quickstarts/8_entity_framework.html或包含表更改的修改集,完成此操作后,运行 IdentityServer 项目,将在目标数据库中创建架构。
Note; OnModelCreating
是通过调用dotnet ef migrations
语法(也称为设计时)以及运行时(如果您调用)Database.Migrate()
在你的 DBContext 上 - 例如MyDbContextInstance.Database.Migrate()
(或异步等效方法)。
如果你想使用自定义的DBContext,那么你可以自定义OnModelCreating
,您需要添加一些在调用时使用的设计时类dotnet ef
从命令行并将新上下文添加到Startup
.
为了完整起见,下面是一个 hacky 粗略示例,其中上下文目标是 PostGres 数据库(使用UseSQLServer
代替UseNpgsql
或者无论您的后备存储是什么(如果不同),连接字符串名称是IDPDataDBConnectionString
在 appsettings.json 文件和本例中的自定义数据库上下文中是MyTestDbContext
它源自 IdentityServer 的ConfigurationDbContext
.
复制粘贴代码,调整路径为appsettings.json
(或重构)然后从命令行执行dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c MyTestDbContext -o Data/Migrations/IdentityServer/ConfigurationDbCreatedWithMyTestContext
您应该看到实体框架使用您放置的任何覆盖生成架构迁移文件OnModelCreating
根据您派生的上下文。下面的例子还包括一些Console.WriteLine
调用以更轻松地跟踪正在发生的事情。
将其添加到Startup
:
services.AddDbContext<MyTestDbContext>(options =>
{
options.UseNpgsql(_configuration.GetConnectionString("IDPDataDBConnectionString"));
});
Note如果您愿意,使用设计时类还允许您将 IdentityServer 数据库迁移文件分离到单独的类库中。确保您的目标是Startup
如果你这样做(参见here https://stackoverflow.com/questions/38705694/add-migration-with-different-assembly了解更多信息)。
namespace MyIdentityServer.DataClassLibrary.DbContexts
{
public class MyTestDbContext : ConfigurationDbContext
{
public MyTestDbContext(DbContextOptions<ConfigurationDbContext> options, ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Console.WriteLine("OnModelCreating invoking...");
base.OnModelCreating(modelBuilder);
// Map the entities to different tables here
modelBuilder.Entity<IdentityServer4.EntityFramework.Entities.ApiResource>().ToTable("mytesttable");
Console.WriteLine("...OnModelCreating invoked");
}
}
public class MyTestContextDesignTimeFactory : DesignTimeDbContextFactoryBase<MyTestDbContext>
{
public MyTestContextDesignTimeFactory()
: base("IDPDataDBConnectionString", typeof(MyTestContextDesignTimeFactory).GetTypeInfo().Assembly.GetName().Name)
{
}
protected override MyTestDbContext CreateNewInstance(DbContextOptions<MyTestDbContext> options)
{
var x = new DbContextOptions<ConfigurationDbContext>();
Console.WriteLine("Here we go...");
var optionsBuilder = newDbContextOptionsBuilder<ConfigurationDbContext>();
optionsBuilder.UseNpgsql("IDPDataDBConnectionString", postGresOptions => postGresOptions.MigrationsAssembly(typeof(MyTestContextDesignTimeFactory).GetTypeInfo().Assembly.GetName().Name));
DbContextOptions<ConfigurationDbContext> ops = optionsBuilder.Options;
return new MyTestDbContext(ops, new ConfigurationStoreOptions());
}
}
/* Enable these if you just want to host your data migrations in a separate assembly and use the IdentityServer supplied DbContexts
public class ConfigurationContextDesignTimeFactory : DesignTimeDbContextFactoryBase<ConfigurationDbContext>
{
public ConfigurationContextDesignTimeFactory()
: base("IDPDataDBConnectionString", typeof(ConfigurationContextDesignTimeFactory).GetTypeInfo().Assembly.GetName().Name)
{
}
protected override ConfigurationDbContext CreateNewInstance(DbContextOptions<ConfigurationDbContext> options)
{
return new ConfigurationDbContext(options, new ConfigurationStoreOptions());
}
}
public class PersistedGrantContextDesignTimeFactory : DesignTimeDbContextFactoryBase<PersistedGrantDbContext>
{
public PersistedGrantContextDesignTimeFactory()
: base("IDPDataDBConnectionString", typeof(PersistedGrantContextDesignTimeFactory).GetTypeInfo().Assembly.GetName().Name)
{
}
protected override PersistedGrantDbContext CreateNewInstance(DbContextOptions<PersistedGrantDbContext> options)
{
return new PersistedGrantDbContext(options, new OperationalStoreOptions());
}
}
*/
public abstract class DesignTimeDbContextFactoryBase<TContext> :
IDesignTimeDbContextFactory<TContext> where TContext : DbContext
{
protected string ConnectionStringName { get; }
protected String MigrationsAssemblyName { get; }
public DesignTimeDbContextFactoryBase(string connectionStringName, string migrationsAssemblyName)
{
ConnectionStringName = connectionStringName;
MigrationsAssemblyName = migrationsAssemblyName;
}
public TContext CreateDbContext(string[] args)
{
return Create(
Directory.GetCurrentDirectory(),
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"),
ConnectionStringName, MigrationsAssemblyName);
}
protected abstract TContext CreateNewInstance(
DbContextOptions<TContext> options);
public TContext CreateWithConnectionStringName(string connectionStringName, string migrationsAssemblyName)
{
var environmentName =
Environment.GetEnvironmentVariable(
"ASPNETCORE_ENVIRONMENT");
var basePath = AppContext.BaseDirectory;
return Create(basePath, environmentName, connectionStringName, migrationsAssemblyName);
}
private TContext Create(string basePath, string environmentName, string connectionStringName, string migrationsAssemblyName)
{
var builder = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile(@"c:\change\this\path\to\appsettings.json")
.AddJsonFile($"appsettings.{environmentName}.json", true)
.AddEnvironmentVariables();
var config = builder.Build();
var connstr = config.GetConnectionString(connectionStringName);
if (String.IsNullOrWhiteSpace(connstr) == true)
{
throw new InvalidOperationException(
"Could not find a connection string named 'default'.");
}
else
{
return CreateWithConnectionString(connstr, migrationsAssemblyName);
}
}
private TContext CreateWithConnectionString(string connectionString, string migrationsAssemblyName)
{
if (string.IsNullOrEmpty(connectionString))
throw new ArgumentException(
$"{nameof(connectionString)} is null or empty.",
nameof(connectionString));
var optionsBuilder =
new DbContextOptionsBuilder<TContext>();
Console.WriteLine(
"MyDesignTimeDbContextFactory.Create(string): Connection string: {0}",
connectionString);
optionsBuilder.UseNpgsql(connectionString, postGresOptions => postGresOptions.MigrationsAssembly(migrationsAssemblyName));
DbContextOptions<TContext> options = optionsBuilder.Options;
Console.WriteLine("Instancing....");
return CreateNewInstance(options);
}
}
}
边注;如果您已经有了一个包含 IdentityServer 表的数据库,您可以手动重命名它们,忽略实体框架迁移 - 您唯一需要的就是更改Startup
to AddConfigurationStore
and AddOperationalStore
.