Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/258.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 更改IdentityServer4实体框架表名称_C#_Postgresql_Entity Framework_Entity Framework Core_Identityserver4 - Fatal编程技术网

C# 更改IdentityServer4实体框架表名称

C# 更改IdentityServer4实体框架表名称,c#,postgresql,entity-framework,entity-framework-core,identityserver4,C#,Postgresql,Entity Framework,Entity Framework Core,Identityserver4,我正在尝试更改PersistedGrantDb和ConfigurationDb为IdentityServer4创建的默认表名,并让实体框架生成正确的SQL。例如我不使用使用表ApiResources的实体IdentityServer4.EntityFramework.Entities.ApiResource,而是希望将数据映射到名为mytesttable 根据定义,这应该很简单,只需在DBContext的OnModelCreating方法中为我想要重新映射的每个实体添加ToTable调用,以覆盖

我正在尝试更改PersistedGrantDb和ConfigurationDb为IdentityServer4创建的默认表名,并让实体框架生成正确的SQL。例如我不使用使用表
ApiResources
的实体
IdentityServer4.EntityFramework.Entities.ApiResource
,而是希望将数据映射到名为
mytesttable

根据定义,这应该很简单,只需在
DBContext的
OnModelCreating
方法中为我想要重新映射的每个实体添加
ToTable
调用,以覆盖TableName=EntityName的默认行为。问题是,这确实创建了一个表
mytesttable
,但实体框架在运行时创建的SQL仍然在查询中使用
ApiResources
,因此失败

我采取的步骤是创建了一个从IdentityServer的
ConfigurationDbContext
派生的
DBContext
,以便能够覆盖
OnModelCreating
并自定义表名:

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);

        modelBuilder.Entity<IdentityServer4.EntityFramework.Entities.ApiResource>().ToTable("mytesttable");

        base.OnModelCreating(modelBuilder);

        Console.WriteLine("...OnModelCreating invoked");
    }
}
这将很高兴地使用
NpgSQL
提供程序在我的PostGRES数据库中漫游并创建必要的表,包括名为
mytesttable
的表,代替实体
IdentityServer4.EntityFramework.Entities.ApiResource
ApiResources。但是,当我从IdentityServer实例调用命令时,生成的SQL仍然引用
ApiResources
,而不是
mytesttable

  Failed executing DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
  SELECT x."Id", x."Description", x."DisplayName", x."Enabled", x."Name"
  FROM "ApiResources" AS x
  ORDER BY x."Id"
  Npgsql.PostgresException (0x80004005): 42P01: relation "ApiResources" does not exist

感谢您的帮助。

这个答案有两个部分:;首先,需要在IdentityServer的配置中调整表名,以便它使用新表名生成查询。其次需要修改实体框架生成的模式,以便它知道如何为Identity framework实体创建不同名称的表。继续读

所以,首先,;更改实体框架查询中使用的表名的功能在挂起
AddIdentityServer
中间件方法的
AddOperationalStore
AddConfigurationStore
方法上公开。提供给配置方法的委托的
options
参数公开表名,例如:
options.{EntityName}.Name={WhateverTableNameYouWantToUse}
-或
options.ApiResource.Name=mytesttable
。您还可以通过调整
schema
属性,在每个表的基础上重写架构

下面的示例使用反射更新所有实体,以使用前缀为
idn\u
的表名,因此
idn\u ApiResources
idn\u ApiScopes
等:

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
PeristedGrandDBContext
,然后重写
OnModelCreating
方法,将每个IdentityServer实体重新映射到修改后的表名,然后创建初始迁移或更新的迁移(流畅的Api语法),您可以根据教程部分的说明,从提供的IdentityServer DBContext的
ConfigurationDbContext
PersistedGrandDBContext
创建初始迁移,然后只需在创建的迁移文件中的所有表名和对这些表名的引用上使用文本编辑器进行查找和替换

无论选择哪种方法,您仍然需要使用
dotnet ef migrations…
命令行语法来创建中所示的初始迁移文件或表更改的修改集,完成后,运行IdentityServer项目,并在目标数据库中创建模式

OnModelCreating
通过
dotnet ef migrations
语法(即在设计时)调用,如果在DBContext上调用
Database.Migrate()
,也可以在运行时调用,例如
MyDbContextInstance.Database.Migrate()
(或异步等效方法)

如果要使用自定义DBContext以便可以自定义
OnModelCreating
,则需要添加一些设计时类,这些类在从命令行调用
dotnet ef
时使用,并将新上下文添加到
启动

为了完整起见,下面是一个粗略的示例,其中上下文目标是PostGres数据库(使用
UseSQLServer
代替
UseNpgsql
或任何与之不同的备份存储)appsettings.json文件中的连接字符串名称是
IDPDataDBConnectionString
,本例中的自定义数据库上下文是
MyTestDbContext
,它源自IdentityServer的
ConfigurationDbContext

复制并粘贴代码,将路径调整为
appsettings.json
(或重构)然后从命令行execute
dotnet ef migrations添加InitialIdentityServerConfigurationDbMigration-c MyTestDbContext-o Data/migrations/IdentityServer/ConfigurationDbCreatedWithMyTestContext
,您应该看到实体框架使用您在其中放置的任何覆盖生成架构迁移文件
在派生上下文上创建模型。下面的示例还包括一些
Console.WriteLine
调用,以便更轻松地跟踪正在发生的事情

将此添加到
启动

 services.AddDbContext<MyTestDbContext>(options =>
        {
            options.UseNpgsql(_configuration.GetConnectionString("IDPDataDBConnectionString"));
        }); 
旁注;如果您已经有了一个包含IdentityServer表的数据库,您可以忽略EntityFrameworks迁移手动重命名它们-然后您只需要将
Startup
更改为
AddConfigurationStore
AddOperationalSto
 services.AddDbContext<MyTestDbContext>(options =>
        {
            options.UseNpgsql(_configuration.GetConnectionString("IDPDataDBConnectionString"));
        }); 
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);
    }
}

}