Get connection string in .NET Core application - c#

I'm trying to get connection string dynamically from appsettings.json file. I see that I can do that via Configuration property of startup class. I've marked Configuration field as static field and access it across the app.
I'm wondering if there is a better way to get connection string value from .NET Core app.

You can check my blog article on ASP.NET Core Configuration here.
In it I also go through Dependency Injection of configuration options.
Quote:
There are a couple ways to get settings. One way would be to use the
Configuration object in Startup.cs.
You can make the configuration available in your app globally through
Dependency Injection by doing this in ConfigureServices in Startup.cs:
services.AddSingleton(Configuration);

You can do a thread-safe Singleton of your IConfiguration variable declared in the Startup.cs file.
private static object syncRoot = new object();
private static IConfiguration configuration;
public static IConfiguration Configuration
{
get
{
lock (syncRoot)
return configuration;
}
}
public Startup()
{
configuration = new ConfigurationBuilder().Build(); // add more fields
}

private readonly IHostingEnvironment _hostEnvironment;
public IConfiguration Configuration;
public IActionResult Index()
{
return View();
}
public ViewerController(IHostingEnvironment hostEnvironment, IConfiguration config)
{
_hostEnvironment = hostEnvironment;
Configuration = config;
}
and in class that you want connection string
var connectionString = Configuration.GetConnectionString("SQLCashConnection");

Related

ASP.NET - Get the value of environment variables passed with docker

In my docker-compose.yaml, I'm setting environment variables to an api service with
env_file:
- db.env
db.env
MYSQL_DATABASE=db_name
MYSQL_ROOT_PASSWORD=root
MYSQL_USER=user
MYSQL_PASSWORD=pass
How to access them to connect to the database with a string like this?
string _connectionString = $"server={Env["MYSQL_SERVER_NAME"]}; database={Env["MYSQL_DATABASE"]}; user={Env["MYSQL_USER"]}; password={Env["MYSQL_PASSWORD"]}";
This is an invaluable reference: Configuration in .Net - essentially, a lot of the plumbing is already done for you.
So, if for example you wanted to set that in your startup Program.cs:
var builder = WebApplication.CreateBuilder(args);
....
string _connectionString = $"server={builder.Configuration["MYSQL_SERVER_NAME"]}...
With DI, in a MVC controller for example:
public class SomeController: Controller
{
public SomeController(ILogger<HomeController> logger, IConfiguration configuration)
{
string _connectionString = $"server={configuration["MYSQL_SERVER_NAME"]}....
In a Razor Page:
public class HelloModel: PageModel
{
public HelloModel(ILogger<HomeController> logger, IConfiguration configuration)
{
string _connectionString = $"server={configuration["MYSQL_SERVER_NAME"]}....
Hth..

How to get ConnectionString Value from appsetting.json into Repository in Core WebAPI

I'm new to Net Core WebAPI, I'm developing Core Web API without EF. I got stuck were i'm not able to get Connection String from appsetting.json into my Repository
I have created Constructor of CommonRepository and used IConfiguration to get connection string value
public CommonRepository(IConfiguration configuration)
{
_configuration = configuration;
}
private readonly IConfiguration _configuration;
but while creating object of common repository it is giving an error
CommonRepository commonRepository =new CommonRepository();
There is no argument given that corresponds to the required formal parameter 'configuration' of 'CommonRepository.CommonRepository(IConfiguration)'
You could refer the following code to create and use the CommonRepository, then, get the connection string.
CommonRepository:
public interface ICommonRepository
{
string GetConnectionstring();
}
public class CommonRepository : ICommonRepository
{
private readonly IConfiguration _configuration;
public CommonRepository(IConfiguration configuration)
{
_configuration = configuration;
}
public string GetConnectionstring()
{
return _configuration.GetConnectionString("DefaultConnection");
}
}
Register the above repository in the Startup.ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<ICommonRepository, CommonRepository>();
}
Controller:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly ICommonRepository _commonRepositoty;
public HomeController(ILogger<HomeController> logger, ICommonRepository commonRepository)
{
_logger = logger;
_commonRepositoty = commonRepository;
}
public IActionResult Index()
{
var str = _commonRepositoty.GetConnectionstring();
return View();
}
The result like this:
More detail information, see Dependency injection in ASP.NET Core.
This is normally done using dependency injection which should be setup in the startup.cs file. As such:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
}
}
ASP.Net will automatically pass in the config to this class from the host. We use this to create a singleton which will inject the config into all constructors that use IConfiguration as a parameter. Explained Here

Need a way to get the current request URL to configure the database context in multi-tenant application

I am migrating a web app from asp.net mvc to .net core (.net 5), and this has got me stuck.
The site is configured in IIS to accept request from multiple URLs like site1.example.com and site2.example.com. Each site has its own database, accessed through entity framework core.
In the old .net framework, I was able to use one of the events in the global.asax.cs to parse the incoming request URL and lookup the correct tenant database from a configuration file. I'm trying to set up something similar in asp.net core mvc.
Here's the relevant part of my ConfigureServices method in the startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<ITenantIdentifier, UrlTenantIdentifier>();
services.AddDbContext<myDbContext>((serviceProvider, dbContextBuilder) =>
{
var tenantIdentifier = serviceProvider.GetRequiredService<ITenantIdentifier>();
var connectionString = Configuration.GetConnectionString(tenantIdentifier.GetCurrentTenantId() + "myDataModel");
dbContextBuilder.UseSqlServer(connectionString);
}, ServiceLifetime.Scoped);
//other services configured below...
}
Then the tenant identifier looks like this:
public interface ITenantIdentifier
{
string GetCurrentTenantId();
}
public class UrlTenantIdentifier : ITenantIdentifier
{
readonly IHttpContextAccessor _httpContextAccessor;
readonly ILogger<UrlTenantIdentifier> _logger;
public UrlTenantIdentifier(IHttpContextAccessor httpContextAccessor, ILogger<UrlTenantIdentifier> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public string GetCurrentTenantId()
{
//_httpContextAccessor is null here
//logic below for parsing URL and finding if we're site1 or site2
}
}
Is there a correct way of doing this now that I'm not aware of? How can I set up the entity framework database context for dependency injection when I don't know the connection string key until runtime? Am I going to be stuck configuring separate sites and virtual directories in IIS?
Refactor the DbContext to override the OnConfiguring member. Inject configuration and context accessor and perform configuration there.
public class myDbContext : DbContext {
private readonly ITenantIdentifier tenantIdentifier;
private readonly IConfiguration configuration;
public myDbContext(IConfiguration configuration, ITenantIdentifier tenantIdentifier) {
this.configuration = configuration;
this.tenantIdentifier = tenantIdentifier;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
var connectionString = configuration
.GetConnectionString(tenantIdentifier.GetCurrentTenantId() + "myDataModel");
optionsBuilder.UseSqlServer(connectionString);
}
}
Trying to access the request context at the time the DbContext is being created/initialized is too early in the request flow to get access to the desired information. It needs to happen after the context has already been initialized and injected.
public void ConfigureServices(IServiceCollection services)
services.AddHttpContextAccessor();
services.AddSingleton<ITenantIdentifier, UrlTenantIdentifier>();
services.AddDbContext<myDbContext>(); //Simplified since configuration is internal
//other services configured below...
}
Reference DbContext Lifetime, Configuration, and Initialization

Get instance of singleton service in startup

I have the following situation:
startup.cs
services.AddSingleton<IConfigurationManager, ConfigurationManager>();
ConfigurationManager configurationManager = new ConfigurationManager();
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(configurationManager.DatabaseConnectionString));
So, in order to create my context I need the connection string which this configurationManager provides to me. However, I would still like to keep the ConfigurationManager as a service.
Is there a way to do this without explicitly instantiating the configurationManager or is it perhaps even fine to leave it like this?
It's possible to access the IServiceProvider while building the context:
services.AddDbContext<MyContext>((serviceProvider, options) =>
{
var configManager = serviceProvider.GetService<IConfigurationManager>();
options.UseSqlServer(configManager.DatabaseConnectionString);
});
However, here your best options might be to read the Iconfiguration injected in Startup.cs:
public class Startup
{
public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
HostingEnvironment = env;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("MyContext"));
});
}
}
You can use the service provider to get instances of services:
Build the services provider -
var provider = services.BuildServiceProvider();
Get specific service -
provider.GetService<T>();
Although a better option would be to use the IConfiguration .NET Core provides - https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1
Using this method you can set IConfiguration to parse your config settings in the Startup method. From there you can then inject these settings into the required classes.
Also you can do sth similar to this.
I am not familiar with what you do with your configuration manager to provide a precise answer.
Basically you can do a pre-config inside your Program.cs.
Build your configuration here. As you can see i am passing IConfigurationBuilder.
Program.cs
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(AddDbConfiguration)
.UseStartup<Startup>();
private static void AddDbConfiguration(WebHostBuilderContext context, IConfigurationBuilder builder)
{
var configuration = builder.Build(); // you could instantiate you Configuration manager here and append to the configuration.
var connectionString = configuration.GetConnectionString("Database");
builder.AddDemoDbProvider(options => options.UseSqlServer(connectionString));
}
source: https://medium.com/#dneimke/custom-configuration-in-net-core-2-193ff6f02046

Using configuration settings in nested classes

When I have nested classes where children need some of the configuration settings (i.e., settings written in appsettings.json), do I need to make a bucket relay to pass configuration to children classes?
I don't think the example below is a smart way. Is there any better practice?
Startup.cs
public Startup(IConfiguration configuration, ...)
{
...
this.Configuration = configuration;
...
}
Parent.cs
public class Parent
{
public Parent(IConfiguration configuration)
{
_configuration = configuration;
}
private IConfiguration _configuration;
private ChildOne _childOne;
private ChildTwo _childTwo;
public void InitializeChildren()
{
_childOne = new ChildOne(_configuration);
_childTwo = new ChildTwo(_configuration);
}
}
ChildOne.cs
public class ChildOne{
public ChildOne(IConfiguration configuration){
_propOne = configuration.GetSection("brahbrah").Value;
}
private string _propOne;
}
Domain objects / models are nothing more than data containers. These data containers can have a need for data but should not be dependent on dependency injection (directly) for this data because they are at the core of your application. A change in your model (or it's dependencies) will most likely result in bigger changes.
As you show in your examples you want to instantiate your models using the new operator and pass the IConfiguration as a parameter. By requiring IConfiguration in your data container you create a situation where your model will need extensive checking if the returned result exists and if every property in it exists and afterward setting the appropriate values in the data container.
A better solution to this problem is by registering a dedicated config class, which we will call BrahBrahConfig to match your example, in the dependency injection framework.
public static IServiceCollection SetupDependencyInjection(this
IServiceCollection services, IConfiguration config)
{
services.Configure<BrahBrahConfig>(config.GetSection("brahbrah"));
return services;
}
In the example above you see the use of an overload for IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config) which can be found in the nuget package "Microsoft.Extensions.Options.ConfigurationExtensions".
This overload enables you to directly inject an instance of IOptions into the constructor of your choice.
private BrahBrahConfig _config;
public Parent(IOptions<BrahBrahConfig> config)
{
_config = config?.Value ?? throw new ArgumentNullException(nameof(config));
}
So after registering this in your startup.cs you can use IOptions as parameter in the Parent constructor and use these settings to set the appropriate properties in your model.
Use dependency injection for your services, not for your Models. Models should not have any logic or service registration.
If you are talking about Service Classes, they are generally part of DI. you can register them to DI so that DI automatically resolves services upon instance construction.
For Instance,
public class Parent
{
public Parent(IConfiguration configuration, ChildOne childOne, ChildTwo childTwo)
{
_configuration = configuration;
_childOne = childOne;
_childTwo = childTwo;
}
private IConfiguration _configuration;
private ChildOne _childOne;
private ChildTwo _childTwo;
}
If you need to initialize ChildOne and ChildTwo by yourself, then you need to pass IConfiguration parameter or at least IServiceProvider in order to resolve required service(s)

Categories