Как перенести базу данных EFCore из кода с внешней сборкой миграции? - PullRequest
2 голосов
/ 20 февраля 2020

Я создаю приложение, которое включает IdentityServer4, используя. NET Core 3.1 и Entity Framework Core. Предполагается, что он может работать с несколькими типами баз данных.

Следуя различным примерам, мне удалось настроить его как на SQL Server, так и на MySQL, используя две отдельные сборки миграции в дополнение к самому проекту IdentityServer. который содержит DbContexts.

В 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"

Если я использую командную строку do tnet, миграция будет работать правильно

dotnet ef database update -c IdentityServerContext

I wi sh, чтобы иметь возможность использовать конфигурацию или командную строку, чтобы 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 и использовал * 1 028 * чтобы загрузить сборку вручную. Это работает и применяет перенос правильно, но это очень грязный обходной путь.

Может ли кто-нибудь пролить свет на причину сбоя загрузки сборки при вызове из context.Database.Migrate(), но не при использовании инструментов ef или при загрузке вручную в Resolving обратном вызове?


Edit

Я думаю, что нашел обходной путь, который соответствует тому, что мы делаем в другом проекте в том же решении.

Мы динамически загружаем модули в Dot Net Core DI framework во время выполнения в этом проекте, и миграции в модулях работают правильно.

Я добавил Scutor библиотека проекта для сканирования загруженных вручную сборок миграции в коллекцию служб DI. Даже если он не регистрирует какие-либо классы, этого, по-видимому, достаточно для загрузки сборки в AssemblyLoadContext, чтобы код миграции мог ее загрузить.

Однако это не объясняет, почему он не может загрузить сборки миграции автоматически.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...