Получите ConnectionString из appsettings.json вместо жесткого кодирования в приложении .NET Core 2.0

У меня есть следующий класс в приложении NET Core2.0.

// required when local database does not exist or was deleted
public class ToDoContextFactory : IDesignTimeDbContextFactory<AppContext>
{
    public AppContext CreateDbContext(string[] args)
    {
        var builder = new DbContextOptionsBuilder<AppContext>();
        builder.UseSqlServer("Server=localhost;Database=DbName;Trusted_Connection=True;MultipleActiveResultSets=true");
        return new AppContext(builder.Options);
    }
}

Это требуется в Core 2.0 с миграцией, когда база данных не существует и должна быть создана при запуске базы данных обновлений.
Невозможно создать миграцию после обновления до ASP.NET Core 2.0

Я бы не хотел иметь ConnectionString в 2-х местах (здесь и в appsettings.json), но только в .json поэтому я попытался заменить

"Server=localhost;Database=DbName;Trusted_Connection=True;MultipleActiveResultSets=true"

с

ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString

но он не работает, я получаю нулевое значение.

ОБНОВЛЕНИЕ 1:
Просто отметим, что добавление явно .json не требуется в Core 2, поэтому проблема не в файле.
https://andrewlock.net/exploring-program-and-startup-in-asp-net-core-2-preview1-2/

ОБНОВЛЕНИЕ 2:
Также я уже использую Configuration для отправки ConnectionString из .json в контекст:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<AppContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    }
}

Но я не могу использовать это для ToDoContextFactory, потому что у него нет конфигурации, а ToDoContextFactory используется для миграции, поэтому приложение не работает вообще.

РЕШЕНИЕ: Основываясь на ответе от @JRB, я сделал это так:

public AppContext CreateDbContext(string[] args)
{
    string projectPath = AppDomain.CurrentDomain.BaseDirectory.Split(new String[] { @"bin\" }, StringSplitOptions.None)[0];
    IConfigurationRoot configuration = new ConfigurationBuilder()
        .SetBasePath(projectPath)
        .AddJsonFile("appsettings.json")
        .Build();
    string connectionString = configuration.GetConnectionString("DefaultConnection");

    var builder = new DbContextOptionsBuilder<AppContext>();
    builder.UseSqlServer(connectionString);

    return new AppContext(builder.Options);
}

Ответ 1

ШАГ 1: Включите в OnConfiguring()

следующее:
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddJsonFile("appsettings.json")
            .Build();
        optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
    }

ШАГ 2: Создайте appsettings.json:

  {
    "ConnectionStrings": {       
      "DefaultConnection": "Server=YOURSERVERNAME; Database=YOURDATABASENAME; Trusted_Connection=True; MultipleActiveResultSets=true"        
    } 
  }

ШАГ 3: Жесткая копия appsettings.json в правильный каталог

  Hard copy appsettings.json.config to the directory specified in the AppDomain.CurrentDomain.BaseDirectory directory. 
  Use your debugger to find out which directory that is.        

Предположение: вы уже включили пакет Microsoft.Extensions.Configuration.Json(получите его от Nuget) в свой проект.

Ответ 2

В ASPNET Core вы делаете это в Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<BloggingContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("BloggingDatabase")));
}

где ваше соединение определено в appsettings.json

{
  "ConnectionStrings": {
    "BloggingDatabase": "..."
  },
}

Пример из документов MS

Ответ 3

Как пропустить его как dp-инъекцию в этот класс? в ConfigureServices:

services.Configure<MyOptions>(Configuration);

создать класс для хранения строк json:

public class MyOptions
{
    public MyOptions()
    {

    }
    public string Option1 { get; set; }
    public string Option2 { get; set; }
}    

Добавьте строки в json файл:

"option1": "somestring",
"option2": "someothersecretstring"

В классах, которым нужны эти строки, передайте в качестве конструктора:

public class SomeClass
{
 private readonly MyOptions _options;

    public SomeClass(IOptions<MyOptions> options)
    {
        _options = options.Value;           
    }    

 public void UseStrings()
 {
   var option1 = _options.Option1;
    var option2 = _options.Option2;
 //code
 }
}

Ответ 4

Фактически используется шаблон по умолчанию, который можно использовать для достижения этого результата без необходимости выполнять IDesignTimeDbContextFactory и выполнять любое копирование файла конфигурации.

Подробно в этот документ, в котором также обсуждаются другие способы, с помощью которых инфраструктура будет пытаться создать экземпляр DbContext при проектировании время.

В частности, вы используете новый крючок, в данном случае статический метод формы public static IWebHost BuildWebHost(string[] args). Документация подразумевает в противном случае, но этот метод может жить в любом классе, где находится ваша точка входа (см. Src). Реализация этого является частью руководства в документе 1.x to 2.x, и то, что не совсем очевидно, смотрит на код, заключается в том, что вызов WebHost.CreateDefaultBuilder(args) - это, помимо прочего, подключение вашей конфигурации в шаблоне по умолчанию, с которого начинаются новые проекты. Это все, что вам нужно, чтобы настроить конфигурацию для служб времени разработки, таких как миграции.

Здесь более подробно о том, что происходит в глубине:

При добавлении миграции, когда структура пытается создать ваш DbContext, он сначала добавляет любые реализации IDesignTimeDbContextFactory, которые он находит набор методов factory, которые можно использовать для создания вашего контекста, затем он получает ваши настроенные службы через статический крючок, обсуждаемый ранее, и выглядит для любых типов контекстов, зарегистрированных с помощью DbContextOptions (который происходит в вашем Startup.ConfigureServices при использовании AddDbContext или AddDbContextPool) и добавляет эти заводы. Наконец, просматривает через сборку для любых DbContext производных классов и создает метод factory, который просто вызывает Activator.CreateInstance как окончательный приветствую Мэри.

Порядок приоритета, который использует каркас, такой же, как и выше. Таким образом, если вы выполнили IDesignTimeDbContextFactory, он переопределит упомянутый выше крючок. Однако для большинства распространенных сценариев вам не понадобится IDesignTimeDbContextFactory.

Ответ 5

Я понимаю, что это было помечено как ответ, но я столкнулся с небольшой проблемой, когда работал над проектом, в котором мой уровень доступа к базовым данным EF в проекте .DLL отделен от остальной части моего проекта, API, Auth и Сеть и в основном понравятся мои другие проекты, чтобы ссылаться на этот проект данных. И я не хочу заходить в проект Data, чтобы каждый раз менять строки подключения.

ШАГ 1. Включите это в метод OnConfiguring.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
      {
           var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
           IConfigurationRoot configuration = new ConfigurationBuilder()
                **.SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))**
                .AddJsonFile("appsettings.json", optional: false)
                .AddJsonFile($"appsettings.{envName}.json", optional: false)
                .Build();
           optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
      }

ПРИМЕЧАНИЕ..SetBasePath(Path.Combine(Directory.GetCurrentDirectory())) Это сведет на нет или сделает недействительной необходимость копирования файла в каталог, поскольку ASP.NET CORE достаточно умен, чтобы выбрать правильный файл. Также указанная среда выберет правильный файл при сборке для выпуска или производства, при условии, что выбран файл среды Prod.

ШАГ 2: Создайте appsettings.json

{
"ConnectionStrings": {       
  "DefaultConnection": "Server=YOURSERVERNAME; Database=YOURDATABASENAME; Trusted_Connection=True; MultipleActiveResultSets=true"        
} 

}

ПОЖАЛУЙСТА: Ссылка: Microsoft.Extensions.Configuration

Ответ 6

Не хватает нескольких вещей, как из приведенных выше решений, так и из документации Microsoft. Если вы перейдете по ссылке на репозиторий GitHub, ссылка на которую приведена в документации выше, вы найдете реальное решение.

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

Итак, в Startup.cs добавьте:

 public IConfiguration Configuration { get; }
 public Startup(IConfiguration configuration) 
 {
     Configuration = configuration;
 }

а затем в методе ConfigureServices добавьте то, что сказали другие люди...

services.AddDbContext<ChromeContext>(options =>                    
    options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection")));

вы также должны убедиться, что у вас создан файл appsettings.json и есть раздел строк подключения, аналогичный этому

{
  "ConnectionStrings": {
    "DatabaseConnection": "Server=MyServer;Database=MyDatabase;Persist Security Info=True;User ID=SA;Password=PASSWORD;MultipleActiveResultSets=True;"
  }
}

Конечно, вам придется изменить это, чтобы отразить вашу конфигурацию.

Вещи, чтобы иметь в виду. Это было протестировано с использованием Entity Framework Core 3 в проекте .Net Standard 2.1. Мне нужно было добавить пакеты nuget для: Microsoft.EntityFrameworkCore 3.0.0 Microsoft.EntityFrameworkCore.SqlServer 3.0.0, потому что то, что я использую, и то, что требуется для получения доступа к UseSqlServer.

Ответ 7

Вы также можете сделать это в ASP.NET Core 2, определив строку подключения в файле appSettings.json. Затем в Startup.cs вы указываете, какую строку подключения использовать.

appSettings.json

{
    "connectionStrings": {
        "YourDBConnectionString": "Server=(localdb)\\mssqllocaldb;Database=YourDB;Trusted_Connection=True"
    }
}

Startup.cs

public static IConfiguration Configuration { get; private set;}

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}
var connectionString = Configuration["connectionStrings:YourDBConnectionString"];
services.AddDbContext<YourDbContext>(x => x.UseSqlServer(connectionString));

Ответ 8

  1. Добавьте следующий код в файл startup.cs.

    public void ConfigureServices(IServiceCollection services)
    {
        string con = Configuration.GetConnectionString("DBConnection");
        services.AddMvc();
        GlobalProperties.DBConnection = con;//DBConnection is a user defined static property of GlobalProperties class
    }
    
  2. Используйте свойство GlobalProperties.DBConnection в классе Context.

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {  
              optionsBuilder.UseSqlServer(GlobalProperties.DBConnection);
        }
    }
    

Ответ 9

Я не знаю, что вы знаете, но вы можете использовать класс обратного вызова, создать hostbuilder и установить для конфигурации статическое свойство.

Для ядра ASP 2.2:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System;

namespace Project
{
    sealed class Program
    {
        #region Variables
        /// <summary>
        /// Last loaded configuration
        /// </summary>
        private static IConfiguration _Configuration;
        #endregion

        #region Properties
        /// <summary>
        /// Default application configuration
        /// </summary>
        internal static IConfiguration Configuration
        {
            get
            {
                // None configuration yet?
                if (Program._Configuration == null)
                {
                    // Create the builder using a callback class
                    IWebHostBuilder builder = WebHost.CreateDefaultBuilder().UseStartup<CallBackConfiguration>();

                    // Build everything but do not initialize it
                    builder.Build();
                }

                // Current configuration
                return Program._Configuration;
            }

            // Update configuration
            set => Program._Configuration = value;
        }
        #endregion

        #region Public
        /// <summary>
        /// Start the webapp
        /// </summary>
        public static void Main(string[] args)
        {
            // Create the builder using the default Startup class
            IWebHostBuilder builder = WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();

            // Build everything and run it
            using (IWebHost host = builder.Build())
                host.Run();
        }
        #endregion


        #region CallBackConfiguration
        /// <summary>
        /// Aux class to callback configuration
        /// </summary>
        private class CallBackConfiguration
        {
            /// <summary>
            /// Callback with configuration
            /// </summary>
            public CallBackConfiguration(IConfiguration configuration)
            {
                // Update the last configuration
                Program.Configuration = configuration;
            }

            /// <summary>
            /// Do nothing, just for compatibility
            /// </summary>
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                //
            }
        }
        #endregion
    }
}

Итак, теперь вы просто используете статическую Program.Configuration в любом другом классе, который вам нужен.