Read appsettings.json - Fields remain null - c#

Think I have a problem with the startup.cs as I do not get any values from my <IOption> config
So.. We have our appsettings.json
"Config": {
"ApplicationName": "some name",
"ConnectionString": "someconstring",
"Version": "1.0.0"
},
Here we have our model
public class Config
{
public string ApplicationName { get; set; }
public string ConnectionString { get; set; }
public string Version { get; set; }
}
The startup.cs
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public static IConfiguration Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Add functionality to inject IOptions<T>
services.AddOptions();
// Add our Config object so it can be injected
services.Configure<Config>(Configuration);
}
And then in our controller I try to load those data but unfortunately they remain empty.
private IOptions<Config> config;
public CompaniesController(IOptions<Config> config)
{
this.config = config;
}
I've tried to change the startup.cs with something like
services.Configure<Config>(options =>
{
options.ConnectionString = Configuration.GetSection("Config:ConnectionString").Value;
});
but that doesn't seems to work.
Resources I've been using:
https://dzone.com/articles/dynamic-connection-string-in-net-core
https://stackoverflow.com/questions/31453495/how-to-read-appsettings-values-from-json-file-in-asp-net-core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.2
but Obviously I am missing a crucial point here.
edit: I am using ASP.Net Core 2.0
edit2:
the Program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
The entire Appsettings.json file
{
"Config": {
"ApplicationName": "somename",
"ConnectionString": "someconstring",
"Version": "1.0.0"
},
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
edit 3:
In my front-end application I import the API like this.

services.Configure<Config>(Configuration);
This line doesn't achieve the desired result because the JSON properties you're looking for are nested under a Config property in your appsettings.json file. To load these values as intended, use GetSection to grab the Config section and pass that into the Configure<TOptions> method:
services.Configure<Config>(Configuration.GetSection("Config"));

services.Configure<Config>(options =>
{
options.ConnectionString = Configuration.GetValue<string>("Config:ConnectionString");
options.ApplicationName = "test";
});
If you want to configure your options more granuarly.

Related

C# Console App, pass multiple parameters to custom ILogger

Searching I've not found the answer I'm looking for. I will only put code snippets here to help ask my question, supplying ALL of the code would not be executable without the entire system.
I'm using the built-in logging in C# to log to Windows Event Viewer and/or the Console. I also wanted to write to a file but not third-party logging, so I wrote my own simple logger that logs the same data to files and works.
My appsettings.json as a second for some configuration parameters like a working folder. I also have a folder path for the logger. What I would like to be able to do is use the same holder path from the setting and not have 2.
appsettings.json
{
"AppSettings": {
"WorkingFolderPath": "C:\\mypath\\",
"HeartbeatIntervalMinutes": 0, // This is seconds when in debug mode.
"FileRetryDelayMinutes": 1,
"PingTimeoutMilliseconds": 200
},
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Debug"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Debug"
}
},
"EventLog": {
"LogLevel": {
"Default": "Warning"
}
},
"FileLog": {
"Options": {
"FolderPath": "C:\\mypath\\",
"RetentionDays": 5
},
"LogLevel": {
"Default": "Debug"
}
}
}
}
As you see in this file I have a "WorkingFolderPath" setting I'd like to use that as my path in my "FileLog" logger and not have to specify a path there as well.
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
logging.AddConsole();
logging.AddDebug();
logging.AddEventLog(new EventLogSettings()
{
SourceName = "FreshIQAppMessagingService"
});
logging.AddFileLogger(options =>
{
context.Configuration.GetSection("Logging").GetSection("FileLog").GetSection("Options").Bind(options);
});
})
.ConfigureAppConfiguration((hostContext, config) =>
{
config
.SetBasePath(ApplicationPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureServices((hostContext, services) =>
{
services.Configure<Configuration>(hostContext.Configuration.GetSection("AppSettings"));
services.AddHostedService<Service1>();
services.AddHostedService<Service2>();
services.AddHostedService<Service3>();
services.AddHostedService<Service4>();
});
Here it's getting the options and passing them in. What I've not been able to figure out is how to change my classes so that I can send in the "WorkingFolderPath" from the top of the appsettings instead of the one with the options with the FileLog logger.
logging.AddFileLogger(options =>
{
context.Configuration.GetSection("Logging").GetSection("FileLog").GetSection("Options").Bind(options);
});
FileLoggerExtensions.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace FreshIQAppMessaging.Logging
{
public static class FileLoggerExtensions
{
public static ILoggingBuilder AddFileLogger(this ILoggingBuilder loggingBuilder, Action<FileLoggerOptions> configure)
{
loggingBuilder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
loggingBuilder.Services.Configure(configure);
return loggingBuilder;
}
}
}
FileLoggerProvider.cs
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.IO;
namespace FreshIQAppMessaging.Logging
{
[ProviderAlias("FileLog")]
public class FileLoggerProvider : ILoggerProvider
{
public readonly FileLoggerOptions Options;
public FileLoggerProvider(IOptions<FileLoggerOptions> options)
{
Options = options.Value;
if (!Directory.Exists(Options.FolderPath))
{
Directory.CreateDirectory(Options.FolderPath);
}
}
public ILogger CreateLogger(string categoryName)
{
return new FileLogger(this);
}
public void Dispose() { }
}
}
FileLogger.cs
using Microsoft.Extensions.Logging;
using System;
using System.Globalization;
using System.IO;
namespace FreshIQAppMessaging.Logging
{
public class FileLogger : ILogger
{
protected readonly FileLoggerProvider _fileLoggerProvider;
public FileLogger(FileLoggerProvider fileLoggerProvider)
{
_fileLoggerProvider = fileLoggerProvider;
}
public IDisposable? BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel != LogLevel.None;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
// Clean up old log files
var logFiles = Directory.GetFiles(_fileLoggerProvider.Options.FolderPath, "*-MyApp.log");
foreach (var logFilePath in logFiles)
{
var logFileName = new FileInfo(logFilePath).Name;
if (DateTime.TryParseExact(logFileName.Substring(0, 8), "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var logFileDate))
{
if (logFileDate.AddDays(_fileLoggerProvider.Options.RetentionDays) < DateTime.Now)
{
File.Delete(logFilePath);
}
}
}
var fullFilePath = $"{_fileLoggerProvider.Options.FolderPath}\\{DateTime.Now.ToString("yyyyMMdd")}-MyApp.log";
var logRecord = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} [{logLevel.ToString()}] {formatter(state, exception)} {(exception != null ? exception.StackTrace : "")}";
using (var streamWriter = new StreamWriter(fullFilePath, true))
{
streamWriter.WriteLine(logRecord);
}
}
}
}
FileLoggerOptions.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace FreshIQAppMessaging.Logging
{
public class FileLoggerOptions
{
public virtual string? FolderPath { get; set; }
public virtual int RetentionDays { get; set; } = 5;
}
}
You typically don't want to include custom options for your logger inside the Logging section. Most people do something like this instead:
{
"AppSettings": {
"WorkingFolderPath": "C:\\mypath\\",
...
},
"FileLog": {
"FolderPath": "C:\\mypath\\",
"RetentionDays": 5
},
"Logging": {
...
"FileLog": {
"LogLevel": {
"Default": "Debug"
}
}
}
}
You didn't share your options class but I assume you have a property on it named FolderPath. You could then configure your file logger like this:
logging.Services.Configure<FileLoggerOptions>(builder.Configuration.GetSection("FileLog"));
logging.AddFileLogger(options =>
{
options.FolderPath = builder.Configuration["AppSettings:WorkingFolderPath"];
});
You can now remove the FolderPath property from the FileLog object in the appsettings.json file since that value will be set using this line when adding the file logger:
options.FolderPath = builder.Configuration["AppSettings:WorkingFolderPath"];
The options would then look like this:
{
"AppSettings": {
"WorkingFolderPath": "C:\\mypath\\",
...
},
"FileLog": {
"RetentionDays": 5
},
"Logging": {
...
"FileLog": {
"LogLevel": {
"Default": "Debug"
}
}
}
}
I've written a similar logging provider in the Elmah.Io.Extensions.Logging repository and I copied some of the code from there. I tried mapping it to your original code in the question, but there might be something that doesn't match exactly. I hope that you still see the intent behind what I'm trying. You can look inside this repository for inspiration.

How to use Serilog to write file for each client?

I am making .Net core application and use Serilog for logging. Now I want to use Serilog to write a log for each clients who already logged in.
I expected the application gonna have seperate logger
Global logger: write server log
Client loggers: each client gonna have their own logger to write log file.
From what I tried to do the application do generate the txt file. However, it doesn't write anything to it.
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseIISIntegration()
.UseSerilog((hostingcontext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingcontext.Configuration))
.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<LoggerManager>();
}
...MORE CODE...
}
LoggerManager.cs
public class LoggerManager
{
public static Dictionary<string, ILogger> loggerDict = new Dictionary<string, ILogger>();
public void CreateUserLogger(string username, EnumType userType)
{
var logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Async(a => a.File(#$"Log/{userType}/{username}.txt",
buffered:true,
rollingInterval:RollingInterval.Day,
retainedFileCountLimit:90))
.CreateLogger();
loggerDict.TryAdd(cheID, logger);
}
public void WriteInfoLog (string username, string message)
{
loggerDict.TryGetValue(username, out ILogger logger);
logger.Information(message);
}
}
HomeController.cs
class HomeController {
private readonly LoggerManager _loggerManager;
public HomeController(LoggerManager loggerManager)
{
_loggerManager = loggerManager;
}
public IActionResult Index() {
_loggerManager.WriteInfoLog(authenticatedUser.CHEID, "HELLO HOW ARE U TODAY");
return View();
}
}
appsettings.json
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"System": "Warning",
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"path": "Logs/Server/serverlog.txt",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"buffered": true
}
}
]
}
}
]
}
I randomly found the "sinks-map". This sinks definitely satisfy my requirement.
https://github.com/serilog/serilog-sinks-map

Issues reading appsettings.json file C# .NET Core

I'm coming from regular .NET Web API and I have found the experience around configuration in .NET Core 2 absolutely maddening.
I have read the official documentation and a few tutorials such as this one, but they all see to error.
I have a Database helper class that is supposed to establish a MongoDB connection.
My configuration is rather simple
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "mongodb://localhost:27017"
}
}
I started with the .NET Core official Angular (ngx) boilerplate template
I add the configuration as a service singleton in Startup.CS under the ConfigureServices section like so services.AddSingleton(Configuration);
I attempt to inject the configuration into my class like so
public class DatabaseHelper
{
public static string connstring { get; private set; }
public DatabaseHelper(IConfiguration Configuration)
{
connstring = Configuration["ConnectionStrings:DefaultConnectionString"];
}
public static IMongoDatabase QuizDB { get; } = GetDatabase("QuizEngine");
public static IMongoCollection<Quiz> QuizCol { get; } = GetQuizCollection();
public static MongoClient GetConnection() {
return new MongoClient(connstring);
}
public static IMongoDatabase GetDatabase(string database) {
MongoClient conn = DatabaseHelper.GetConnection();
return conn.GetDatabase(database);
}
public static IMongoCollection<Quiz> GetQuizCollection() {
return DatabaseHelper.QuizDB.GetCollection<Quiz>("Quizzes");
}
}
This compiles and builds fine with no Intellesense errors - But when I step through it in the debugger, the connstring is null. I have tried playing around with the configuration names etc, but I always seem to come up empty.
I have also tried the POCO way using the example code in the below answer, but I seem to run into static field initializer issues.
Getting value from appsettings.json in .net core
if it helps here is the the startup class part where it sets the public value of Configuration
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
You don't need to add the configuration as a service singleton in Startup.CS under the ConfigureServices section like services.AddSingleton(Configuration);
If you separate your solution into multiple projects with use of class libraries, Microsoft.Extensions.Options.ConfigurationExtensions package comes in handy for reading the values from appsettings files and injecting them into your configuration classes within projects.
It has 2 extensions you can use:
public static T Get<T>(this IConfiguration configuration);
public static IServiceCollection Configure<TOptions>(this IServiceCollection services,
IConfiguration config) where TOptions : class;
your configuration file appsetting.json
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MongoDBConnection": "mongodb://localhost:27017"
}
}
add code for ConfigureServices method in startup.cs file
services.Configure<MongoDBconfig>(Configuration.GetSection("ConnectionStrings"));
services.AddSingleton<DatabaseHelper>();
your DBhelper.cs
public class MongoDBconfig
{
public string MongoDBConnection { get; set; }
}
public class DatabaseHelper
{
public static string connstring { get; private set; }
public DatabaseHelper(IOptions<MongoDBconfig> Configuration)
{
connstring = Configuration.Value.MongoDBConnection;
}
public static IMongoDatabase QuizDB { get; } = GetDatabase("QuizEngine");
public static IMongoCollection<Quiz> QuizCol { get; } = GetQuizCollection();
public static MongoClient GetConnection()
{
return new MongoClient(connstring);
}
public static IMongoDatabase GetDatabase(string database)
{
MongoClient conn = DatabaseHelper.GetConnection();
return conn.GetDatabase(database);
}
public static IMongoCollection<Quiz> GetQuizCollection()
{
return DatabaseHelper.QuizDB.GetCollection<Quiz>("Quizzes");
}
}
Try the following way using this extension method GetConnectionString
public DatabaseHelper(IConfiguration Configuration)
{
connstring = Configuration.GetConnectionString("DefaultConnection");
}
Try this
public DatabaseHelper(IConfiguration Configuration)
{
string test = Configuration.GetSection("ConnectionStrings")["DefaultConnection"];
Console.WriteLine(test);
}
Check if this able to extract the value in your appsettings.json

ASP .NET Core 2.1 get variables from appsettings.json without Startup.cs

This is my appsettings.json. It is located in a class library that does not have a startup.cs. I'm wondering if its possible to get the variables out from there to be used locally?
{
"AccountsAddress": "http://localhost:55260/api/Accounts/",
"ApplicationUsersAddress": "http://localhost:55260/api/ApplicationUsers/"
}
public static class Manager
{
public static IConfiguration AppSetting { get; }
static Manager()
{
AppSetting = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
}
}
This is the structure of my library
{
"AccountsAddress": "http://localhost:55260/api/Accounts/",
"ApplicationUsersAddress": "http://localhost:55260/api/ApplicationUsers/",
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
Create An myConfig.cs file and paste following code.
using Microsoft.Extensions.Configuration;
static class ConfigurationManager
{
public static IConfiguration AppSetting { get; }
static ConfigurationManager()
{
AppSetting = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("YouAppSettingFile.json")
.Build();
}
}
Now you can use it like :
string value = ConfigurationManager.AppSetting["value_Key"];

How to read connection string in .NET Core?

I want to read just a connection string from a configuration file and for this add a file with the name "appsettings.json" to my project and add this content on it:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-
WebApplica71d622;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
On ASP.NET I used this:
var temp=ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
Now how can I read "DefaultConnection" in C# and store it on a string variable in .NET Core?
The posted answer is fine but didn't directly answer the same question I had about reading in a connection string. Through much searching I found a slightly simpler way of doing this.
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
// Add the whole configuration object here.
services.AddSingleton<IConfiguration>(Configuration);
}
In your controller add a field for the configuration and a parameter for it on a constructor
private readonly IConfiguration configuration;
public HomeController(IConfiguration config)
{
configuration = config;
}
Now later in your view code you can access it like:
connectionString = configuration.GetConnectionString("DefaultConnection");
You can do this with the GetConnectionString extension-method:
string conString = Microsoft
.Extensions
.Configuration
.ConfigurationExtensions
.GetConnectionString(this.Configuration, "DefaultConnection");
System.Console.WriteLine(conString);
or with a structured-class for DI:
public class SmtpConfig
{
public string Server { get; set; }
public string User { get; set; }
public string Pass { get; set; }
public int Port { get; set; }
}
Startup:
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// http://developer.telerik.com/featured/new-configuration-model-asp-net-core/
// services.Configure<SmtpConfig>(Configuration.GetSection("Smtp"));
Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure<SmtpConfig>(services, Configuration.GetSection("Smtp"));
And then in the home-controller:
public class HomeController : Controller
{
public SmtpConfig SmtpConfig { get; }
public HomeController(Microsoft.Extensions.Options.IOptions<SmtpConfig> smtpConfig)
{
SmtpConfig = smtpConfig.Value;
} //Action Controller
public IActionResult Index()
{
System.Console.WriteLine(SmtpConfig);
return View();
}
with this in appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-WebApplica71d622;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Smtp": {
"Server": "0.0.0.1",
"User": "user#company.com",
"Pass": "123456789",
"Port": "25"
}
See link for more info:
https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-strings
JSON
{
"ConnectionStrings": {
"BloggingDatabase": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
}
C# Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BloggingContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BloggingDatabase")));
}
EDIT: aspnetcore, starting 3.1:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1
This is how I did it:
I added the connection string at appsettings.json
"ConnectionStrings": {
"conStr": "Server=MYSERVER;Database=MYDB;Trusted_Connection=True;MultipleActiveResultSets=true"},
I created a class called SqlHelper
public class SqlHelper
{
//this field gets initialized at Startup.cs
public static string conStr;
public static SqlConnection GetConnection()
{
try
{
SqlConnection connection = new SqlConnection(conStr);
return connection;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
At the Startup.cs I used ConfigurationExtensions.GetConnectionString to get the connection,and I assigned it to SqlHelper.conStr
public Startup(IConfiguration configuration)
{
Configuration = configuration;
SqlHelper.connectionString = ConfigurationExtensions.GetConnectionString(this.Configuration, "conStr");
}
Now wherever you need the connection string you just call it like this:
SqlHelper.GetConnection();
In .NET Core 6
appsettings.json
"ConnectionStrings": {
"DefaultConnection": "Server=**Server Name**;Database=**DB NAME**;Trusted_Connection=True;MultipleActiveResultSets=true"
}
Program.cs
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));
DB Context
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}
The way that I found to resolve this was to use AddJsonFile in a builder at Startup (which allows it to find the configuration stored in the appsettings.json file) and then use that to set a private _config variable
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();
_config = builder.Build();
}
And then I could set the configuration string as follows:
var connectionString = _config.GetConnectionString("DbContextSettings:ConnectionString");
This is on dotnet core 1.1
ASP.NET Core (in my case 3.1) provides us with Constructor injections into Controllers, so you may simply add following constructor:
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
private readonly IConfiguration m_config;
public TestController(IConfiguration config)
{
m_config = config;
}
[HttpGet]
public string Get()
{
//you can get connection string as follows
string connectionString = m_config.GetConnectionString("Default")
}
}
Here what appsettings.json may look like:
{
"ConnectionStrings": {
"Default": "YOUR_CONNECTION_STRING"
}
}
In 3.1 there is a section already defined for "ConnectionStrings"
System.Configuration.ConnnectionStringSettings
Define:
"ConnectionStrings": {
"ConnectionString": "..."
}
Register:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ConnectionStringSettings>(Configuration.GetSection("ConnectionStrings"));
}
Inject:
public class ObjectModelContext : DbContext, IObjectModelContext
{
private readonly ConnectionStringSettings ConnectionStringSettings;
...
public ObjectModelContext(DbContextOptions<ObjectModelContext> options, IOptions<ConnectionStringSettings> setting) : base(options)
{
ConnectionStringSettings = setting.Value;
}
...
}
Use:
public static void ConfigureContext(DbContextOptionsBuilder optionsBuilder, ConnectionStringSettings connectionStringSettings)
{
if (optionsBuilder.IsConfigured == false)
{
optionsBuilder.UseLazyLoadingProxies()
.UseSqlServer(connectionStringSettings.ConnectionString);
}
}
The method below will work fine if you want to get a connectionString from appsettings.json into a Model or ViewModel (not Controller). This is for ASP.NET Core 3 and above. Sometimes you may need to get a connectionString into a Model (for SQL queries) rather than dependency injection via the controller so this method below will get your connectionString from appsettings:
public class NameOfYourModel
{
static class getConnection
{
public static IConfigurationRoot Configuration;
public static string GetConnectionString()
{
var builder = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
var connectionString =
Configuration.GetConnectionString("connectionStringName");
return connectionString;
}
}
string connStr = getConnection.GetConnectionString().ToString(); //This
//line now has your connectionString which you can use.
//Continue the rest of your code here.
}
There is another approach. In my example you see some business logic in repository class that I use with dependency injection in ASP .NET MVC Core 3.1.
And here I want to get connectiongString for that business logic because probably another repository will have access to another database at all.
This pattern allows you in the same business logic repository have access to different databases.
C#
public interface IStatsRepository
{
IEnumerable<FederalDistrict> FederalDistricts();
}
class StatsRepository : IStatsRepository
{
private readonly DbContextOptionsBuilder<EFCoreTestContext>
optionsBuilder = new DbContextOptionsBuilder<EFCoreTestContext>();
private readonly IConfigurationRoot configurationRoot;
public StatsRepository()
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
configurationRoot = configurationBuilder.Build();
}
public IEnumerable<FederalDistrict> FederalDistricts()
{
var conn = configurationRoot.GetConnectionString("EFCoreTestContext");
optionsBuilder.UseSqlServer(conn);
using (var ctx = new EFCoreTestContext(optionsBuilder.Options))
{
return ctx.FederalDistricts.Include(x => x.FederalSubjects).ToList();
}
}
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"EFCoreTestContext": "Data Source=DESKTOP-GNJKL2V\\MSSQLSERVER2014;Database=Test;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Too late, but after reading all helpful answers and comments, I ended up using Microsoft.Extensions.Configuration.Binder extension package and play a little around to get rid of hardcoded configuration keys.
My solution:
IConfigSection.cs
public interface IConfigSection
{
}
ConfigurationExtensions.cs
public static class ConfigurationExtensions
{
public static TConfigSection GetConfigSection<TConfigSection>(this IConfiguration configuration) where TConfigSection : IConfigSection, new()
{
var instance = new TConfigSection();
var typeName = typeof(TConfigSection).Name;
configuration.GetSection(typeName).Bind(instance);
return instance;
}
}
appsettings.json
{
"AppConfigSection": {
"IsLocal": true
},
"ConnectionStringsConfigSection": {
"ServerConnectionString":"Server=.;Database=MyDb;Trusted_Connection=True;",
"LocalConnectionString":"Data Source=MyDb.db",
},
}
To access a strongly typed config, you just need to create a class for that, which implements IConfigSection interface(Note: class names and field names should exactly match section in appsettings.json)
AppConfigSection.cs
public class AppConfigSection: IConfigSection
{
public bool IsLocal { get; set; }
}
ConnectionStringsConfigSection.cs
public class ConnectionStringsConfigSection : IConfigSection
{
public string ServerConnectionString { get; set; }
public string LocalConnectionString { get; set; }
public ConnectionStringsConfigSection()
{
// set default values to avoid null reference if
// section is not present in appsettings.json
ServerConnectionString = string.Empty;
LocalConnectionString = string.Empty;
}
}
And finally, a usage example:
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// some stuff
var app = Configuration.GetConfigSection<AppConfigSection>();
var connectionStrings = Configuration.GetConfigSection<ConnectionStringsConfigSection>();
services.AddDbContext<AppDbContext>(options =>
{
if (app.IsLocal)
{
options.UseSqlite(connectionStrings.LocalConnectionString);
}
else
{
options.UseSqlServer(connectionStrings.ServerConnectionString);
}
});
// other stuff
}
}
To make it neat, you can move above code into an extension method.
That's it, no hardcoded configuration keys.
private readonly IConfiguration configuration;
public DepartmentController(IConfiguration _configuration)
{
configuration = _configuration;
}
[HttpGet]
public JsonResult Get()
{
string sqlDataSource = configuration["ConnectionStrings:DefaultConnection"];
You can use configuration extension method : getConnectionString ("DefaultConnection")
https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/Extensions/Configuration/ConfigurationExtensions/index.html#Microsoft.Extensions.Configuration.ConfigurationExtensions.GetConnectionString
i have a data access library which works with both .net core and .net framework.
the trick was in .net core projects to keep the connection strings in a xml file named "app.config" (also for web projects), and mark it as 'copy to output directory',
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="conn1" connectionString="...." providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
ConfigurationManager.ConnectionStrings - will read the connection string.
var conn1 = ConfigurationManager.ConnectionStrings["conn1"].ConnectionString;

Categories