IdentityServer - ConfigurationDbContext でのクロスコンテキストトランザクション

ConfigurationDbContext を使ってクロスコンテキストトランザクションを処理するには?

Joni 【ジョニー】

IdentityServer とは?

IdentityServer is a free, open source OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Founded and maintained by Dominick Baier and Brock Allen, IdentityServer4 incorporates all the protocol implementations and extensibility points needed to integrate token-based authentication, single-sign-on and API access control in your applications.

詳細はこちらのリンク↓↓↓

IdentityServer4 の構成データ (Configuration Data) を EF Core を使って DB に保存することはできます。構成データは「リソース」と「クライアント」を管理します。

EF Core での構成データの詳細についてはこちら↓↓↓


本題に入ります。

例えば、ビジネスルールのニーズで、アプリケーションの DB Context でデータを追加する必要があり、それと同時にクライアントを追加する必要があるとします。つまり、セットで追加で処理する必要があります。

クライアントのクラスの定義は ConfigurationDbContext に存在します。ctor の定義はこんな感じ:

public ConfigurationDbContext(DbContextOptions<TContext> options, ConfigurationStoreOptions storeOptions)
: base(options)

この ConfigurationDbContext は IdentityServer のもので、アプリケーションの DB Context と異なるため、それぞれを dbContext.SaveChanges() (あるいは await dbContext.SaveChangesAsync() )で処理すると、仮に例外が発生した場合に整合性がとれなくなってしまいます。

EF Core でクロスコンテキストトランザクションはサポートされていますので、実現可能です。

条件としては、

Sharing a DbConnection requires the ability to pass a connection into a context when constructing it.

The easiest way to allow DbConnection to be externally provided, is to stop using the DbContext.OnConfiguring method to configure the context and externally create DbContextOptions and pass them to the context constructor.

そして、

DbContextOptionsBuilder is the API you used in DbContext.OnConfiguring to configure the context, you are now going to use it externally to create DbContextOptions.

だそうです。

簡単そうですね!

サンプルコードはこんな感じで DbContextOptions を生成します。

var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(new SqlConnection(connectionString))
.Options;

それを ConfigurationDbContext ctor に渡せばいいよね~!!と喜ぶのも束の間・・・

ctor の2番目のパラメータって、 ConfigurationStoreOptions を渡しますよね。DI でこれを気にする必要がなく、やってくれていますので、正しいパラメータはわかりません。。。適当に渡してみてエラーが発生します。デバッグとかで少し時間があればできそうですが、簡単な方法でやりたいです!
そういえば、アプリケーションの DB Context って我々が自由に触れるので、まずそれを使いましょう!ctor は DbContextOptions を渡せるようにします。

こちらのサンプルで話しますと、 context1ConfigurationDbContext に例えて、それをそのまま DI からのインスタンスを使い、14行目の:

using (var context2 = new BloggingContext(options))

をこんな感じで context1 の DB コネクションを context2 と共有させます↓

using (var context2 = new ApplicationDbContext(
new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(context1.Database.GetDbConnection())
.Options))

はい、これでシンプルで問題解決!

Joni 【ジョニー】

Written by

Web / .NET since 2001 beta / ASP.NET Core / Docker / Twitter: @joni2nja / https://joni.carrd.co/

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade