.NET Core 3.1 と Entity Framework Core を使用して、IdentityServer4 を含むアプリケーションを作成しています。複数のデータベース タイプをターゲットにできるように意図されています。
さまざまな例に従って、DbContexts を含む IdentityServer プロジェクト自体に加えて、2 つの個別の移行アセンブリを使用して、SQL Server と MySQL の両方で動作させることができました。
Startup.cs には次のものがあります。
var migrationsAssembly = $"IdentityServer.Migrations.{serverType}";
services.AddDbContext<Data.IdentityServerContext>(options =>
{
switch (serverType)
{
case ServerType.SqlServer:
options.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
break;
case ServerType.MySql:
options.UseMySql(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
break;
}
});
ServerType は、「SqlServer」または「MySql」の構成列挙型です。
dotnet コマンド ラインを使用すると、移行は正しく機能します
dotnet ef database update -c IdentityServerContext
構成またはコマンドラインを使用して、IdentityServer に移行自体を適用できるようにしたいと考えています。
例 Main()
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var provider = scope.ServiceProvider;
try
{
var configuration = provider.GetRequiredService<IConfiguration>();
if (configuration.GetValue<bool>("migrateDatabases"))
{
var identityContext = provider.GetRequiredService<IdentityServerContext>();
identityContext.Database.Migrate();
}
}
catch
{
}
}
host.Run();
ただし、これを行うと例外が発生します。
System.IO.FileNotFoundException: Could not load file or assembly 'IdentityServer.Migrations.MySql, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
File name: 'IdentityServer.Migrations.MySql, Culture=neutral, PublicKeyToken=null'
at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, RuntimeAssembly assemblyContext, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, AssemblyLoadContext assemblyLoadContext)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, StackCrawlMark& stackMark, AssemblyLoadContext assemblyLoadContext)
at System.Reflection.Assembly.Load(AssemblyName assemblyRef, StackCrawlMark& stackMark, AssemblyLoadContext assemblyLoadContext)
at System.Reflection.Assembly.Load(AssemblyName assemblyRef)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsAssembly..ctor(ICurrentDbContext currentContext, IDbContextOptions options, IMigrationsIdGenerator idGenerator, IDiagnosticsLogger`1 logger)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetRelationalService[TService](IInfrastructure`1 databaseFacade)
at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
at IdentityServer.Program.Main(String[] args)
移行アセンブリは、プロジェクトの DLL および EXE と同じディレクトリにあります。
解決できないのは、移行アセンブリを読み込めない理由です。
実験として、コールバックを接続し、アセンブリを手動でロードするためにAssemblyLoadContext.Default.Resolving
使用しました。Assembly.LoadFrom(<path>)
これは機能し、移行を正しく適用しますが、非常に厄介な回避策です。
context.Database.Migrate()
から呼び出されたときにアセンブリの読み込みが失敗するのに、ef ツールを使用したときやコールバックで手動で読み込まれたときに失敗する理由を誰かが明らかにすることはできResolving
ますか?
編集
同じソリューションの別のプロジェクトで行っていることと一致する回避策を見つけたと思います。
そのプロジェクトの実行時に Dot Net Core DI フレームワークにモジュールを動的にロードしており、モジュールの移行は正しく機能しているように見えます。
ライブラリをプロジェクトに追加してScutor
、手動で読み込まれた移行アセンブリを DI サービス コレクションにスキャンしました。クラスは登録されていませんがAssemblyLoadContext
、移行コードがアセンブリをロードできるようにするには、アセンブリをロードするのに十分なようです。
ただし、これは移行アセンブリを自動的にロードできない理由を説明していません。