EF Core миграции не может использовать секретный менеджер
Когда я создаю основные веб-приложения.net, я использую секретный менеджер во время тестирования. Как правило, я могу создать новый веб-проект (mvc и web api), щелкнуть правой кнопкой мыши по проекту и выбрать "Управление секретами пользователя". Это открывает файл JSON, где я добавляю секреты. Затем я использую это в моем startup.cs примерно так:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration["connectionString"]));
Сайт отлично с этим работает и хорошо подключается к базе данных. Однако, когда я пытаюсь использовать команды переноса ядра ef, такие как add-migration
похоже, что они не могут получить доступ к строке соединения из секретного менеджера. Я получаю сообщение об ошибке "Строка подключения не может быть нулевой". Ошибка исчезла, когда я жесткий код Configuration["connectionString"]
с фактической строкой. Я проверил онлайн и проверил файл.csproj, они уже содержат следующие строки:
<UserSecretsId>My app name</UserSecretsId>
И позже:
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
Что мне нужно добавить, чтобы миграции могли получить доступ к строке подключения?
Обновить
У меня есть только один конструктор в классе контекста:
public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options) : base(options)
{
}
2 ответа
В настоящее время я тоже сталкиваюсь с этой проблемой. Я придумала решение, которое работает на данный момент, но в лучшем случае можно считать грязным.
Я создал класс конфигурации, который предоставляет интерфейс конфигурации по запросу:
public static class Configuration
{
public static IConfiguration GetConfiguration()
{
return new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, true)
.AddUserSecrets<Startup>()
.AddEnvironmentVariables()
.Build();
}
}
В Миграции вы можете получить Файл конфигурации и получить доступ к его UserSecrets следующим образом:
protected override void Up(MigrationBuilder migrationBuilder)
{
var conf = Configuration.GetConfiguration();
var secret = conf["Secret"];
}
Я проверил создание сценария SQL с этими пользовательскими секретами, и он работает (очевидно, вы не захотите оставлять сценарий лежащим в стороне, поскольку он будет раскрывать действительный секрет).
Обновить
Приведенный выше конфиг также можно настроить в классе Program.cs в BuildWebHost
метод:
var config = new ConfigurationBuilder().AddUserSecrets<Startup>().Build();
return WebHost.CreateDefaultBuilder(args).UseConfiguration(config)...Build()
Или в конструкторе запуска при использовании этой конвенции
Обновление 2 (объяснение)
Оказывается, эта проблема связана с тем, что сценарии миграции выполняются с установленной средой "Производство". Секретный менеджер настроен на работу только в среде разработки (по уважительной причине). .AddUserSecrets<Startup>()
Функция просто добавляет секреты для всей среды.
Чтобы убедиться, что это не установлено для вашего производственного сервера, есть два решения, которые я заметил, одно из них предлагается здесь: https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell
Установите env:ASPNETCORE_ENVIRONMENT перед запуском, чтобы указать среду ASP.NET Core.
Это решение будет означать, что нет необходимости устанавливать .AddUserSecrets<Startup>()
на каждый проект, созданный на компьютере в будущем. Однако, если вам приходится делить этот проект с другими компьютерами, его необходимо настроить на каждом компьютере.
Второе решение состоит в том, чтобы установить .AddUserSecrets<Startup>()
только на отладочной сборке вот так:
return new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, true)
#if DEBUG
.AddUserSecrets<Startup>()
#endif
.AddEnvironmentVariables()
.Build();
Дополнительная информация
Интерфейс конфигурации может быть передан контроллерам в их конструкторе, т.е.
private readonly IConfiguration _configuration;
public TestController(IConfiguration configuration)
{
_configuration = configuration;
}
Таким образом, любые секреты и настройки приложения доступны в этом контроллере путем доступа _configuration["secret"]
,
Однако, если вы хотите получить доступ к секретам приложения, например, из файла миграции, который существует за пределами самого веб-приложения, вам необходимо придерживаться первоначального ответа, поскольку нет простого (насколько мне известно) способа доступа к ним. в противном случае секреты (один из вариантов использования, о котором я могу подумать, это заполнение базы данных администратором и мастер-паролем).
Чтобы использовать миграции в NetCore с пользовательскими секретами, мы также можем установить класс (SqlContextFactory) для создания своего собственного экземпляра SqlContext с использованием указанного компоновщика конфигурации. Таким образом, нам не нужно создавать какой-то обходной путь в наших классах Program или Startup. В приведенном ниже примере SqlContext
это реализация DbContext/IdentityDbContext
,
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
public class SqlContextFactory : IDesignTimeDbContextFactory<SqlContext>
{
public SqlContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false)
.AddUserSecrets<Startup>()
.AddEnvironmentVariables()
.Build();
var builder = new DbContextOptionsBuilder<SqlContext>();
builder.UseSqlServer(config.GetConnectionString("DefaultConnection"));
return new SqlContext(builder.Options);
}
}
Поскольку я заметил, что многие люди попадают в эту путаницу, я пишу упрощенную версию этой резолюции.
Проблема / путаница
Секретный менеджер в ядре.net предназначен для работы только в среде разработки. При запуске вашего приложения ваш файл launchSettings.json гарантирует, что вашASPNETCORE_ENVIRONMENT
переменная установлена в "Развитие". Однако при запуске миграции EF этот файл не используется. В результате, когда вы запускаете миграции, ваше веб-приложение не запускается в среде разработки и, следовательно, не имеет доступа к секретному менеджеру. Это часто вызывает путаницу относительно того, почему миграции EF не могут использовать секретный менеджер.
Разрешение
Убедитесь, что для переменной среды "ASPNETCORE_ENVIRONMENT" на вашем компьютере установлено значение "Разработка".
Способ использования .AddUserSecrets<Startup>()
будет делать циклическую ссылку, если у нас есть наш DbContext в отдельной библиотеке классов и используется DesignTimeFactory
Чистый способ сделать это:
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
#if DEBUG
.AddJsonFile(@Directory.GetCurrentDirectory() + "{project path}/appsettings.Development.json", optional: true, reloadOnChange: true)
#else
.AddJsonFile(@Directory.GetCurrentDirectory() + "{startup project path}/appsettings.json", optional: true, reloadOnChange: true)
#endif
.AddEnvironmentVariables()
.Build();
var connectionString = configuration.GetConnectionString("DefaultConnection");
//Console.WriteLine(connectionString);
var builder = new DbContextOptionsBuilder<AppDbContext>();
Console.WriteLine(connectionString);
builder.UseSqlServer(connectionString);
return new AppDbContext(builder.Options);
}
}
Объяснение:
Secret Manager предназначен только для разработки, поэтому это не повлияет на миграцию в случае, если он находится в конвейере на этапах QA или Production, поэтому для исправления этого мы будем использовать строку подключения разработчика, которая существует appsettings.Development.json
в течение #if Debug
.
Преимущество использования этого способа заключается в разделении ссылок на класс Startup веб-проекта при использовании библиотеки классов в качестве инфраструктуры данных.