web-dev-qa-db-ja.com

カスタム構成DBContextでIdentityServer4を使用する

OracleでIDS4を使用するために、カスタマイズされたIConfigurationDbContextを作成しました。

  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 Migrationを実行すると、次のエラーが発生します。

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を使用する正しい方法ですか?この問題を修正し、移行作業を完了するにはどうすればよいですか?

10
capiono

私は別のアプローチを試しました。 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>();

それは魅力のように機能します。免責事項:これは私の考えではありません。その出所を思い出せません。

2
MJK

最近のリリースでは、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 =>
2
Jay

別のデータベースを使用するように切り替えるために、カスタム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));
               })
               ...;

            ...
        }

        ...
    }
}

構成/運用ストアを独自のプロジェクト/アセンブリに分離しますか?

ソリューションを適切にレイアウトし、構成ストアと運用ストア(およびIDユーザーストア)を独自のクラスライブラリ/アセンブリに分離したい場合はどうなりますか?

ドキュメントによると、-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を実行すると、次のようになります。

enter image description here

2
David Liang

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());
        }
    }
1
capiono

これを行う最も簡単な方法は、以下のようにConfigurationDbContextのパラメーターTを使用することだと思います。それはネットコア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);
    }
}
0
alhpe