Get ConnectionString from appsettings.json instead of being hardcoded in .NET Core 2.0 App

我只是一个虾纸丫 提交于 2019-11-26 19:27:40

问题


I have the following class in NET Core2.0 App.

// 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);
    }
}

This is required in Core 2.0 with migration when Database does not exist and has to be created when you run update-database.
Unable to create migrations after upgrading to ASP.NET Core 2.0

I would like not having ConnectionString in 2 places(here and in appsettings.json) but only in .json so I have tried to replace

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

with

ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString

but it's not working, I'm getting null value.

UPDATE 1:
Just to note that adding explicitly .json is not necessery in Core 2 so the problem is not with the file.
https://andrewlock.net/exploring-program-and-startup-in-asp-net-core-2-preview1-2/

UPDATE 2:
Also I am already using Configuration for sending ConnectionString from .json to Context:

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")));
    }
}

But I can not use this for ToDoContextFactory because it does not have Configuration, and ToDoContextFactory is used by migrations so the App is not running at all.

SOLUTION: Based on answer from @JRB I made it work like this:

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:


STEP 1: Include the following in OnConfiguring()

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddJsonFile("appsettings.json")
            .Build();
        optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
    }

STEP 2: Create appsettings.json:

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

STEP 3: Hard copy appsettings.json to the correct directory

  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.        

Assumption: you have already included package Microsoft.Extensions.Configuration.Json (get it from Nuget) in your project.




回答2:


In ASPNET Core you do it in Startup.cs

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

where your connection is defined in appsettings.json

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

Example from MS docs




回答3:


How about passing it as dp injection into that class? in ConfigureServices:

services.Configure<MyOptions>(Configuration);

create class to hold json strings:

public class MyOptions
{
    public MyOptions()
    {

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

Add strings to json file:

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

In classes that need these strings, pass in as constructor:

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:


There is actually a default pattern that you can employ to achieve this result without having to implement IDesignTimeDbContextFactory and do any config file copying.

It is detailed in this doc, which also discusses the other ways in which the framework will attempt to instantiate your DbContext at design time.

Specifically, you leverage a new hook, in this case a static method of the form public static IWebHost BuildWebHost(string[] args). The documentation implies otherwise, but this method can live in whichever class houses your entry point (see src). Implementing this is part of the guidance in the 1.x to 2.x migration document and what's not completely obvious looking at the code is that the call to WebHost.CreateDefaultBuilder(args) is, among other things, connecting your configuration in the default pattern that new projects start with. That's all you need to get the configuration to be used by the design time services like migrations.

Here's more detail on what's going on deep down in there:

While adding a migration, when the framework attempts to create your DbContext, it first adds any IDesignTimeDbContextFactory implementations it finds to a collection of factory methods that can be used to create your context, then it gets your configured services via the static hook discussed earlier and looks for any context types registered with a DbContextOptions (which happens in your Startup.ConfigureServices when you use AddDbContext or AddDbContextPool) and adds those factories. Finally, it looks through the assembly for any DbContext derived classes and creates a factory method that just calls Activator.CreateInstance as a final hail mary.

The order of precedence that the framework uses is the same as above. Thus, if you have IDesignTimeDbContextFactory implemented, it will override the hook mentioned above. For most common scenarios though, you won't need IDesignTimeDbContextFactory.




回答5:


There are a couple things missing, both from the solutions above and also from the Microsoft documentation. If you follow the link to the GitHub repository linked from the documentation above, you'll find the real solution.

I think the confusion lies with the fact that the default templates that many people are using do not contain the default constructor for Startup, so people don't necessarily know where the injected Configuration is coming from.

So, in Startup.cs, add:

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

and then in ConfigureServices method add what other people have said...

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

you also have to ensure that you've got your appsettings.json file created and have a connection strings section similar to this

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

Of course, you will have to edit that to reflect your configuration.

Things to keep in mind. This was tested using Entity Framework Core 3 in a .Net Standard 2.1 project. I needed to add the nuget packages for: Microsoft.EntityFrameworkCore 3.0.0 Microsoft.EntityFrameworkCore.SqlServer 3.0.0 because that's what I'm using, and that's what is required to get access to the UseSqlServer.




回答6:


I understand this has been marked as answered but I ran into a bit of a problem when I was working on a project where I have my EF Core Data Access Layer in a .DLL Project separated from the rest of my project, API, Auth and Web and mostly will like my other projects to reference this Data project. And I don't want to want to come into the Data project to change connection strings everytime.

STEP 1: Include this in the OnConfiguring Method

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"));
      }

NOTE: .SetBasePath(Path.Combine(Directory.GetCurrentDirectory())) This will negate or invalidate the need to copy the file to a directory as ASP.NET CORE is smart enough to pick the the right file. Also the environment specified will pick right file when the building for Release or Production, assuming the Prod environment file is selected.

STEP 2: Create appsettings.json

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

}

PLEASE: Referece: Microsoft.Extensions.Configuration




回答7:


You can also do this in ASP.NET Core 2 by defining the connection string in your appSettings.json file. Then in your Startup.cs you specify which connection string to use.

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. Add the following code into startup.cs file.

    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. Use GlobalProperties.DBConnection property in Context class.

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



回答9:


It's not fancy I known but you could use a callback class, create a hostbuilder and set the configuration to a static property.

For asp core 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
    }
}

So now on you just use the static Program.Configuration at any other class you need it.



来源:https://stackoverflow.com/questions/45796776/get-connectionstring-from-appsettings-json-instead-of-being-hardcoded-in-net-co

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!