Best practices for config file in netcoreapp1.1 - asp.net-core

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.

Related

Adding console app project to a solution with .Net Core web api

I added reference to the web api project. The added the following to Program.cs
class Program
{
static void Main(string[] args)
{
var builder = new ConfigurationBuilder();
BuildConfig(builder);
var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddDbContext<ModelContext>(
options => options.UseOracle(connectionString)); // dbcontext from web api project
services.AddTransient<IEmailReminderService, EmailReminderService>();
}).Build();
var svc = ActivatorUtilities.CreateInstance<EmailReminderService>();
var er = new EmailReminderService();
er.OpenIncidentReminder();
}
static void BuildConfig(IConfigurationBuilder builder)
{
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true)
.AddEnvironmentVariables();
_configuration = builder.Build();
}
}
I get System.NullReferenceException: 'Object reference not set to an instance of an object.'
_context was null.
Edit
public class EmailReminderService : IEmailReminderService
{
private ModelContext _context;
//private readonly IExceptionLogService _exceptionLogService;
public EmailReminderService(ModelContext con)
{
_context = con;
//_exceptionLogService = exceptionLogService;
}
public void OpenIncidentReminder()
{
var openIncidents = _context.Incidents.ToList();
}
}
#terodaktil is right about the configuration of the DbContext. You need to configure it properly to specify the provider. See Microsoft's docs about this topic.
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) => {
// here i'm using in-memory DB, you'd use SqlServer, Oracle, etc.
services.AddDbContext<AppDbContext>(o => o.UseInMemoryDatabase("db"));
services.AddTransient<IEmailReminderService, EmailReminderService>();
}).Build();
That said, you need to create a scope to resolve a scoped service like DbContext.
Then you can use the scope's service provider directly to resolve IEmailReminderService, or ActivatorUtilities.CreateInstance to create an instance of the concrete implementation EmailReminderService.
using var scope = host.Services.CreateScope();
// now you can resolve the dbcontext
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// or any service that depends on a scoped service
var reminder = scope.ServiceProvider.GetRequiredService<IEmailReminderService>();
// var reminder = ActivatorUtilities.CreateInstance<EmailReminderService>(scope.ServiceProvider);
reminder.OpenIncidentReminder();
References:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#call-services-from-main

How to add configuration settings into _Layout.cshtml shared Razor page

We have this StartUp as follow that getting values from appsettings.json:
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();
var environment = Configuration["ApplicationSettings:Environment"];
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(Configuration.GetSection("ApplicationSettings"));
...
}
And we also model called AppSettings
public class AppSettings
{
public string Environment { get; set; }
public string Version { get; set; }
}
As I'm working on /Pages/Shared/_Layout.cshtml trying to inject this Version into this shared page, I could not do the code behind approach. How do inject this then?
UPDATES 1 -
In theory I could do like this if it's a page model:
public class _LayoutModel : PageModel
{
private readonly AppSettings _appSettings;
public string Version;
public string Environment;
public _LayoutModel(IOptions<AppSettings> appsettings)
{
_appSettings = appsettings.Value;
}
public void OnGet()
{
Environment = _appSettings.Environment;
Version = _appSettings.Version;
}
}
Like in the Controller, you can inject your services into the view too.
You can use #inject like this:
#inject Microsoft.Extensions.Options.IOptions<AppSettings> AppSettingsOptions
or add #using Microsoft.Extensions.Options to the _ViewImports.cs and then inject the options in your layout like this:
#inject IOptions<AppSettings> AppSettingsOptions.
after that you can access your settings like: #AppSettingsOptions.Value.Version.
Adding to Hameed's answer, for Core 3.1:
AppSettings.Json:
{ "Company": {"Name": "EvilCorp"}}
_ViewImports.cshtml:
#using Microsoft.Extensions.Configuration
_Layout.cshtml:
#inject IConfiguration _config
in the Razor html:
<h1>#_config["Company:Name"]</h1>
you can access your settings using ViewData in your _layout.cshtml file.
In your _layout.cshtml file
<title>Version : #ViewData["Version"] | Environment : #ViewData["Environment"]</title>
But you have to set the value of ViewData in the .cshtml file of all pages you are sharing this layout like this
#model YourPageModel
#{
ViewData["Environment"] = Model.Environment;
ViewData["Version"] = Model.Version;
Layout = "/Pages/Shared/_Layout.cshtml";
}
Hope this helps :)

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

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

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

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