4

我正在使用 .NET Core 3.1 和 Entity Framework Core 创建一个包含 IdentityServer4 的应用程序。它的目的是可以针对多种数据库类型。

在各种示例之后,除了包含 DbContexts 的 IdentityServer 项目本身之外,我还通过使用两个单独的迁移程序集,设法让它与 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以便迁移代码能够加载它。

然而,这并不能解释为什么它不能自动加载迁移程序集。

4

0 回答 0