ASP.NET Core how to init connection string in first run-time? - asp.net-core

I am building an enterprise application and i want for the first time the use run the website to be able to choose the SQL server to connect to, and the Database in that server. after that the application always run on that configurations like normal asp.net core app do.
I know that connection string is stored in appsettings.json so i was thinking about changing it's value at run-time after choosing this configuration, but after searching a lot i think this is not possible.
What do suggest is there a way to do this ?

Set reloadOnChange to true when you are adding appsettings.json to ConfigurationBuilder:
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
then create a class for connection string
public class ConnectionString
{
public string Default {get; set;}
}
And in ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.Configure<ConnectionString>(Configuration.GetSection("ConnectionStrings"));
}
Now this configuration is available through dependency injection.
public class HomeController : Controller
{
private readonly ConnectionString _connectionString;
public HomeController(IOptionsSnapshot<ConnectionString> connectionString)
{
_connectionString = connectionString.Value;
}
}
IOptionsSnapshot can reload configuration data when it changes.
More info about Configuration.

You must to load the appsettings.json file in Program.cs
public static void Main(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false)
.Build();
string conStr = configuration.GetConnectionString("DefaultConnection");
// your code
var host = WebHost.CreateDefaultBuilder();
host.Run();
}

Make sure that you have installed following NuGet Packages before connect to the SQL Server Db.
First you have to create connection strings in appsettings.json like below
"ConnectionStrings": {
"DefaultConnection": "Server=DESKTOP-O57GSNN\\MSSQLSERVERNEW,Database=BookListRazor;Trusted_Connection=True;MultipleActiveResultSets=True"},
Here, DefaultConnection is connection Name you are going to use.Please provide any meaningful name you want and it will be use when configuring.Also provide Server Name from SQL Server Management Studio(see the picture below) and Provide a name for your database (Will be created).
Then create ApplicationDbContext.cs file and type below code
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Book> Book { get; set; }
}
Book is the Model in my App.
Finally configure it with SQL Server configuration inside ConfigureServices method in Startup.cs file.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(option => option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddRazorPages().AddRazorRuntimeCompilation();
}
DefaultConnection is connection Name which was already mentioned in appsettings.json

Related

Use settings.json on a Net Core Class Library with EF Core migrations

On a NET Core 2.1 Class Library I have a Entity Framework Core 2.1 DbContext:
public class AppContext : IdentityDbContext<User, Role, Int32> {
public AppContext(DbContextOptions options) : base(options) { }
}
To run migrations on the Class Library I needed to add the following:
public class ContextFactory : IDesignTimeDbContextFactory<Context> {
public Context CreateDbContext(String[] args) {
DbContextOptionsBuilder builder = new DbContextOptionsBuilder<Context>();
builder.UseSqlServer(#"Server=localhost;Database=db;User=sa;Password=pass;");
return new Context(builder.Options);
}
}
With this I am able to run, on the class library commands like:
dotnet ef migrations add "InitialCommit"
dotnet ef database update
But how to move the connection string to a settings.json file in the Class Library?
IDesignTimeDbContextFactory, as its name implies, is strictly for development. There is generally no need to externalize the connection string, because it should be pretty static, even in a team environment. That said, if anything, you should store it in user secrets, because again, this is only for development. Using user secrets keeps the connection string out of your source control, so developers on your team don't step on each other's toes with each other's connection strings.
var config = new ConfigurationBuilder()
.AddUserSecrets()
.Build();
var connectionString = config.GetConnectionString("Foo");
The IDesignTimeDbContextFactory<> implementation is run via the EF utility process. This is a regular console application where you can use Console.Write() and Console.Read() to interact with the user performing migrations and updates. This allows you to prompt the user to enter their connection string at update-time.
public class Builder : IDesignTimeDbContextFactory<AppContext>
{
public AppContext CreateDbContext(string[] args)
{
Console.Write("Enter your connection string: ");
var conStr = Console.ReadLine();
var options = new DbContextOptionsBuilder<AppContext>().UseSqlServer(conStr).Options;
return new AppContext(options);
}
}
You can configure DbContextOptions in startup class and then inject it into context. Within Startup class you can obtain connection string from Configuration.
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<Context>
(options=> options.UseSqlServer(Configuration["ConnectionString:DefaultConnection"]));
....
}
Add you connection string to appsettings.json:
"ConnectionString": {
"DefaultConnection": "your connection string"
},

Store / Retrieve ConnectionString from appSettings.json in ASP.net Core 2 MVC app

I'm looking for the best practice way to store a connection string in appsettings.json in a .net Core 2 MVC app (like you do in web.config in MVC 5).
I want to use Dapper not EF (I found many EF examples).
Something like this:
{
"ConnectionStrings": {
"myDatabase": "Server=.;Database=myDatabase;Trusted_Connection=true;"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
Surely there are many examples online? Nothing I can find that is for .net core 2.0.
Several things have changed between 1 and 2 and I want to ensure I'm using version 2 best practices.
I've found this - but it seems to be .net core 1:
Visual Studio 2017 - MVC Core - Part 05 - Connection String from appsettings.json
This uses key value pair appsettings - not the connectionstrings:
Read AppSettings in ASP.NET Core 2.0
Again it's unclear if this is .net Core 1 or 2: Net Core Connection String Dapper visual studio 2017
Define your connection string(s) in appsettings.json
{
"connectionStrings": {
"appDbConnection": "..."
}
}
Read its value on Startup
If you follow the convention and define your connection string(s) under connectionStrings, you can use the extension method GetConnectionString() to read its value.
public class Startup
{
public IConfiguration Configuration { get; private set; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// Since you said you're using Dapper, I guess you might want to
// inject IDbConnection?
services.AddTransient<IDbConnection>((sp) =>
new SqlConnection(this.Configuration.GetConnectionString("appDbConnection"))
);
// ...
}
}
Use IDbConnection within the repository?
public interface ISpecificationRepository
{
Specification GetById(int specificationId);
}
public SpecificationRepository : ISpecificationRepository
{
private readonly IDbConnection _dbConnection;
public SpecificationRepository(IDbConnection dbConnection)
{
_dbConnection = dbConnection;
}
public Specification GetById(int specificationId)
{
const string sql = #"SELECT * FROM [YOUR_TABLE]
WHERE Id = #specId;";
return _dbConnection
.QuerySingleOrDefault<Specification>(sql,
new { specId = specificationId });
}
}
Just need the connection string in a POCO?
You might use the Options Pattern.
Define a class that exactly matches the JSON object structure in appsettings.json
public class ConnectionStringConfig
{
public string AppDbConnection { get; set; }
}
Register that configuration on Startup
public void ConfigureServices(IServiceCollection services)
{
// ...
services.Configure<ConnectionStringConfig>(
this.Configuration.GetSection("connectionStrings")
);
// ...
}
Receive the accessor in your POCO
public class YourPoco
{
private readonly ConnectionStringConfig _connectionStringConfig;
public YourPoco(IOptions<ConnectionStringConfig> configAccessor)
{
_connectionStringConfig = configAccessor.Value;
// Your connection string value is here:
// _connectionStringConfig.AppDbConnection;
}
}
Notes:
See my sample codes on how to read values from appsettings.json both on Core 1.x and 2.0.
See how I setup if you have more than 1 connection string.
Just put like shown below in appsettings.json.
"ConnectionStrings": {
"DefaultConnection": "Data Source=;Initial Catalog=;Persist Security Info=True;User ID=; Password=;"
}
In Startup.cs fetch it as mentioned below:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
}
Use dependency injection to inject configuration in controller like mentioned below:
public class MyController : Controller
{
private readonly IConfiguration _configuration;
private string connectionString;
public MyController(IConfiguration configuration)
{
_configuration = configuration;
connectionString = _configuration.GetConnectionString("DefaultConnection");
}
}

Best practices for config file in netcoreapp1.1

I have added the access to the appsettings.json file as a framework service in my Startup.cs:
public IConfigurationRoot Configuration { get; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.Configure<AppConfig>(Configuration);
services.AddMvc();
}
So now I have access to the configuration file from my controllers:
public class HomeController : Controller
{
private readonly AppConfig _appConfig;
public HomeController(IOptions<AppConfig> appConfig, ConfigContext configContext)
{
_appConfig = appConfig.Value;
}
}
That's working but what's currently a good practice in netcoreapps for accessing the config file from classes outsite my controller scope?
I mean that I would not like to pass always the required config variables to other methods, example:
public IActionResult AnyAction() {
SomeStaticClass.SomeMethod(_appConfig.var1, _appConfig.var2, _appConfig.var3...)
//or always have to pass the _appConfig reference
SomeStaticClass.SomeMethod(_appConfig)
}
In previous versions of .NET Framework if I required access to the config file from "SomeStaticClass" I used to use ConfigurationManager in any class that I need access to the web.config.
What's the correct way to do it in a netcoreapp1.1 ? either ConfigurationManager like or dependency injection approach works for me.
I think this question is more about how you can get a contextual variable from a static class. I think this will accomplish what you want but I'm not sure why you want a static class or what you are trying to do with it (see XY Problem).
public class HomeController : Controller
{
private readonly AppConfig _appConfig;
public HomeController(IOptions<AppConfig> appConfig, ConfigContext configContext)
{
_appConfig = appConfig.Value;
SomeStaticClass.SomeStaticMember = appConfig.Value
}
public IActionResult AnyAction() {
SomeStaticClass.SomeMethod(); //The get the value you want from within
}
}
EDIT:
You can use Newtonsoft.Json, it's a dependency of Microsoft.AspNet.Mvc.ModelBinding which is a dependency of Microsoft.AspNet.Mvc
string fileContents = string.Empty;
using (StreamReader file = File.OpenText(#".\appsettings.json"))
{
fileContents = file.ReadAllLines();
}
configs= JsonConvert.DeserializeObject(fileContents );
What I did is to create the following class:
public static class Configuration
{
private static IConfiguration _configuration;
static Configuration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
_configuration = builder.Build();
}
public static string GetValue(string key)
{
return _configuration.GetValue<string>(key, null);
}
public static string GetValue(string section, string key)
{
return _configuration.GetSection(section).GetValue<string>(key);
}
}
However it doesn't use the environment logic that is used in Startup.cs using the IHostingEnvironment parameter.

How to change App.config to json config file in .Net Core

my project uses App.config to read config property. Example:
ConfigurationManager.AppSettings["MaxThreads"]
Do you know of a library which I can use to read config from json. Thanks.
The ConfigurationManager static class is not generally available in ASP.NET Core. Instead you should use the new ConfigurationBuilder system and strongly typed configuration.
For example, by default, a configuration is built up in your Startup class using something similar to the following:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
This will load configuration from the appsettings.json file and append the keys the configuration root. If you have an appsettings file like the following:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"ThreadSettings" : {
"MaxThreads" : 4
}
}
Then you can then create a strongly typed ThreadSettings class similar to the following:
public class ThreadSettings
{
public int MaxThreads {get; set;}
}
Finally, you can bind this strongly typed settings class to your configuration by adding a Configure method to your ConfigureServices method.
using Microsoft.Extensions.Configuration;
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ThreadSettings>(Configuration.GetSection("ThreadSettings"));
}
You can then inject and access your settings class from anyother place by injecting it into the constructor. For example:
public class MyFatController
{
private readonly int _maxThreads;
public MyFatController(ThreadSettings settings)
{
maxThreads = settings.MaxThreads;
}
}
Finally, if you really need access to the underlying configuration you can also inject that in ConfigureServices to make it available in your classes.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
}
You can read more about configuration on the docs or on various blogs

Convention based approach for configuring services based on Operating System in Startup.cs

I recently created an ASP.NET service using 1.0.0-rc1-update1 on coreclr (x64). So, the service is capable of running on all supported Operating Systems; very cool! My service just exposes a simple "TODO" API and uses the Entity Framework 7.0 ORM. For persistence, it employs a Sqlite DB on Linux and SQL Server DB on Windows.
I am wondering if there is a convention based approach to allow Startup.cs to handle differing service configurations for the various Operating Systems? For example, my EF configuration differs because it uses Sqlite on Linux and SQL Server on Windows.
The following article does detail some convention based approaches to configuration, but it seems to only allow for different methods for the higher level abstractions of "Development", "Staging", "Production" environments:
https://docs.asp.net/en/latest/fundamentals/environments.html
Currently, I am just injecting the IRuntimeEnviroment in the constructor of Startup.cs and saving it. Then, when ConfigureServices is invoked, I check the OperatingSystem property of the IRuntimeEnvironment and adjust the EF configuration accordingly (my full Startup.cs provided below)...
Any guidance on other (or recommended) approaches would be greatly appreciated.
public class Startup
{
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
public IConfigurationRoot Configuration { get; set; }
public IRuntimeEnvironment RuntimeEnv { get; set; }
public Startup(IHostingEnvironment env, IRuntimeEnvironment runtimeEnv)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
RuntimeEnv = runtimeEnv;
}
public void ConfigureServices(IServiceCollection services)
{
if (RuntimeEnv.OperatingSystem == "Windows")
{
var connectionString = Configuration["Data:DefaultConnection:ConnectionString"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<TodoContext>(options => options.UseSqlServer(connectionString));
}
else if (RuntimeEnv.OperatingSystem == "Linux")
{
var connectionString = Configuration["Data:DefaultConnection:SqlLiteConnection"];
var path = PlatformServices.Default.Application.ApplicationBasePath;
services.AddEntityFramework()
.AddSqlite()
.AddDbContext<TodoContext>(options => options.UseSqlite("Filename=" + Path.Combine(path, "TodoApp.db")));
}
services
.AddMvcCore(options =>
{
options.OutputFormatters.Clear();
options.OutputFormatters.Add(new HttpNotAcceptableOutputFormatter());
options.OutputFormatters.Add(new HttpNoContentOutputFormatter());
})
.AddJsonFormatters();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseIISPlatformHandler();
app.UseMvc();
loggerFactory.AddConsole(minLevel: LogLevel.Verbose);
loggerFactory.MinimumLevel = LogLevel.Debug;
}
}