FluentMigrator с ядром EF и управляемой идентификацией Azure

#c# #azure #entity-framework-core #fluent-migrator

Вопрос:

В моем текущем проекте (.NET 5, ASP.NET Core), мы используем FluentMigrator для запуска миграции ядра EF, которая работает безупречно. Мы находимся в процессе настройки нашей инфраструктуры в Azure для взаимодействия с помощью управляемых удостоверений и установили систему, назначающую управляемые удостоверения из нашей (веб -) службы приложений нашему SQL server.

Мы проследили за этим руководством до конца: https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi?tabs=windowsclient,dotnetcore

Тем не менее, мы получаем следующую ссылку на нулевой указатель от FluentMigrator:

 FluentMigrator.Runner.Processors.SqlServer.SqlServer2016Processor[0]
There was an exception checking if table VersionInfo in (null) exists
System.AggregateException: One or more errors occurred. (Object reference not set to an instance of an object.)
---> System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider.<AcquireTokenInteractiveDeviceFlowAsync>b__18_0(DeviceCodeResult deviceCodeResult)
 at Microsoft.Identity.Client.Internal.Requests.DeviceCodeRequest.ExecuteAsync(CancellationToken cancellationToken)
 at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
 at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.ExecuteAsync(AcquireToken CommonParameters commonParameters, AcquireTokenWithDeviceCodeParameters deviceCodeParameters, CancellationToken cancellationToken)
at Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider.AcquireTokenInteractiveDeviceFlowAsync(IPublicClientApplication app, String[] scopes, Guid connectionId, String userId, SqlAuthenticationMethod authenticationMethod, CancellationTokenSource cts)
at Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider.AcquireTokenAsync(SqlAuthenticationParameters parameters)
--- End of inner exception stack trace ---
at Microsoft.Data.ProviderBase.DbConnectionPool.CheckPoolBlockingPeriod(Exception e)
at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternalamp; connection)
at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternalamp; connection)
at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternalamp; connection)
at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at Microsoft.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
at Microsoft.Data.SqlClient.SqlConnection.Open()
at FluentMigrator.Runner.Processors.GenericProcessorBase.<>c__DisplayClass6_1.<.ctor>b__1()
 

Я бы предположил, что это потому, что FluentMigrator не может подключиться к базе данных с помощью строки подключения:

 "Server=tcp:<server-name>.database.windows.net;Authentication=Active Directory Device Code Flow; Database=<database-name>;"
 

Но я могу ошибаться 🙂

В StartUp.cs мы регистрируем FluentMigrator вот так:

 public void ConfigureServices(IServiceCollection services)
{
    services.AddFluentMigratorCore()
            .ConfigureRunner(rb => rb
                .AddSqlServer()
                .WithGlobalConnectionString(databaseConnectionString)
                .ScanIn(typeof(Startup).Assembly).For.Migrations());
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    using IServiceScope scope = app.ApplicationServices.CreateScope();
        IMigrationRunner runner = scope.ServiceProvider.GetRequiredService<IMigrationRunner>();

        runner.MigrateUp();
}
 

Кто-нибудь знает, возможно ли вообще заставить FluentMigrator работать с объявлениями/управляемыми идентификаторами в Azure?

Любая помощь/подсказка будет высоко оценена.

Заранее спасибо.

РЕДАКТИРОВАТЬ: наш сервис приложений работает нормально, если мы закомментируем регистрацию FluentMigrator.

Комментарии:

1. Мне на самом деле любопытно увидеть вашу реализацию в том, чтобы заставить FluentMigrator запускать миграции EF. Как вы настроили FluentMigrator, чтобы он мог распознавать классы миграции EF?

Ответ №1:

В FluentMigrator вообще нет ничего плохого. Просто у меня было туннельное зрение, и я следовал руководству Microsofts, где они поместили SqlAuthenticationProvider.SetProvider(...) внутреннюю часть services.AddDbContext<T>(...) : -) Перемещение его за пределы AddDbContext<T>(..) , как это, заставило его работать:

 SqlAuthenticationProvider.SetProvider(
            SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow,
            new ManagedIdentitySqlAuthProvider());

services.AddDbContext<BasketApiDbContext>(options =>
{
    options.UseSqlServer(databaseConnectionString);
});