I have a C# console app, and I'm defining an IConfiguration object as follows inside of Program.cs:
var myConfig = new ConfigurationBuilder()
.AddJsonFile($"appsettings.{myEnvironment}.json", true, true)
.Build();
... and I can reference settings from within that same Program.cs file like this:
var mySetting = myConfig.GetValue<string>("Section:SettingName");
However, I'm wondering what would be the way to access that same myConfig object from other files in my application? For instance, myConfig contains a connection string, but I don't actually make use of the connection string until a few layers deeper (inside of myService > myRepository > myDbContext). What is a best practice to read that connection string from inside of myRepository or myDbContext? Obviously, I can pass the connection string as a parameter from Program.cs and myService, but that seems inefficient.
You can use the build-in DI (Dependency Injection), which also works for .NET C# Console Applications as already mentioned here
How to use Dependency Injection in .Net core Console Application
Pass IConfiguration to your constructor. You can use Bind calls to create objects from it, or just read it like a dictionary.
Related
Application is not able to talk to AWS Parameter Store in .NET 6.
It is always talking to appsettings.json.
I tried debugging locally, still same behavior. Not able to find the SystemManagerConfiguration under list of configuration .
var builder = WebApplication.CreateBuilder();
var connectionString = builder.Configuration.GetConnectionString("OrderTrackerDatabase");
Packages Used
Library Source Code : https://github.com/aws/aws-dotnet-extensions-configuration
image
I got the same issue and finally resolved it.
The samples code in https://github.com/aws/aws-dotnet-extensions-configuration missed one line as below after called "AddSystemsManager" method in .Net 6.
builder.Services.Configure<Settings>(builder.Configuration.GetSection($"common:settings"));
After added above line, then I'm able to get the correct values from AWS Parameter Store when using the settings.
I've also created an issue of this in GitHub as below -
https://github.com/aws/aws-dotnet-extensions-configuration/issues/114
I believe the problem might be the trailing slash after "/OrderTracking/", try "/OrderTracking" instead.
WebApplication.CreateBuilder() will create new instance and doesn't carry over the SystemManager configuration.
Instead, use IConfiguration instance through constructor DI.
var connectionString = _configuration.GetConnectionString("OrderTrackerDatabase");
In my case this extensions method was returning null at my lambda:
private static IConfiguration InitializeConfiguration() => new ConfigurationBuilder()
.AddSystemsManager($"/my-data", true, TimeSpan.FromMinutes(5))
.Build();
Because the role of lambda didn't have permission for read SSM for that resource.
User: is not authorized to perform: ssm:GetParametersByPath on resource
So, just add necessary permission (ssm:GetParametersByPath) for the role of lambda to your resource at System Manager.
In my case, I am using lambda serverless, so the IConfig is always null when it is passed to the controller.
I resolved it by changing the IOptions<Settings> settings in the Controller constructor to IConfiguration settings and then access the parameters by name like _settings.GetValue<string>("ParameterName")
A little less "Object Oriented", but it seemed much easier than this complex solution
I am using the options pattern inside of my application, reading properties from my appsettings.json file and using them in classes throughout my application.
However, I have a case where I need to use these properties inside of my program.cs file itself. Here is code which does not work since I am not able to inject an instance of the config within the program.cs file. The code shows what I would like to happen... is there a better way of doing this and still staying within the options pattern?
builder.Services.Configure<SwaggerConfig>(builder.Configuration.GetSection("Swagger"));
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(s =>
{
s.SwaggerEndpoint( SwaggerConfig.Url ,
SwaggerConfig.Name);
s.RoutePrefix = SwaggerConfig.RoutePrefix;
});
You have access to the IServiceProvider within your code. You can obtain the configuration you are looking for with app.Services.GetRequiredService<IOptions<SwaggerConfig>>().Value.
The thing is, once you call builder.Build(), the DI container will be also built.
I'm creating a service that needs to reference legacy DLLs for our ERP system so unfortunately .NET framework is how I need to do this. I tried using a Core Worker Service but my calls to the DLLs won't work.
I like using TopShelf and looked up DI to use with this and Autofac seems to be the way to go and it has TopShelf support. I don't like keeping any settings in our config files so that it's easier to deploy to other environments. We have a whole system that keeps settings tied to the environment you are in (dev, test, prod, etc...). In Core apps I simply inject the connection string in the startup and all is well. In this app I want to inject the connection string when I start the service and be able to spin up a DbContect class at anytime and have it use that connection string.
Since I scaffold my data layer and didn't want to modify the generated DbContext, so I created another partial class (MyContext.Custom.cs) with a constructor that allows me to pass in the connection string
public MyContext(string name) : base(name)
{ }
In my Program.cs I'm adding in an Autofac container to TopShelf
HostFactory.Run(serviceConfig =>
{
serviceConfig.UseAutofacContainer(container);
// etc...
}
The container is being built in a function, I tried following many examples but I can't seem to get my constructor with the parameter to be the one that is used. Here are a few of the ways I've tried. Each method produces no error but it runs my default constructor where it is looking for the named connection string in the config file.
static IContainer BuildContainer()
{
string myConnectionString = AppConfig.Instance.Settings["MyConnectionString"];
var builder = new ContainerBuilder();
//builder.RegisterType<MyContext>()
// .UsingConstructor(typeof(string))
// .WithParameter("name", myConnectionString)
// .InstancePerLifetimeScope();
//builder.Register(c => new MyContext(myConnectionString)).As<MyContext>();
//.WithParameter("name", myConnectionString);
//builder.RegisterType<MyContext>()
// .WithParameter("name", myConnectionString)
// .AsSelf();
//.InstancePerLifetimeScope();
builder.Register(c => new MyContext(myConnectionString)).AsSelf();
builder.RegisterType<MainService>();
return builder.Build();
}
I've tried each variation with .As and .AsSelf(). I've put in the .InstancePerLifetimeScope() and also left it out. I'm not really sure what to set the scope to in this case but figured it should work with lifetime. No matter what I've tried I can't seem to get it to use my constructor.
If I've left out any information feel free to ask and I can fill it in. Hopefully someone has done this. I guess I could pass in the connection string everytime I instantiate a DbContext but I was hoping to get it to work like a Core app which is so much nicer.
thanks
EDIT:
Adding code to show how I'm using my DbContext
public bool Start()
{
using(var db = new MyContext())
{
var warehouse = (from w in db.Warehouses
where w.WarehouseCode == "ABCD"
select w).FirstOrDefault();
}
// other code...
}
I am working on an ASP .NET Core application. i have to change database connection on runtime.
Here i am using appsettings.json file.
Please, I have tries a lot but I nothing seems to be working so I need your help.
For example:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
var configuration = builder.Build();
optionsBuilder.UseMySql(configuration
["ConnectionStrings:Defaultconnection"]);
}
Thanks in advance.
I can think of a couple of ways to do this..
1) Create a DbContextFactory which will create your instances for you. Maybe apply an attribute to each context which will allow you to fetch out the relevant db connection string from your connection string collection at the point that the factory makes the instance? A bit of reflection in the DbContextFactory would do that easy enough.
2) If the collection of DbContexts is not likely to change, and/or the connection strings either - you could register each DbContext manually using the AddDbContext<>() extension method in the ServiceCollection and then just inject the DbContext that you want into the classes you want
I'm trying to configure my services for an ASP.NET Core 2.0 app/website.
I wish to reference some key/values from my appsettings.json file, in this method.
I'm not sure if what I'm going is OK or not:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddCors();
var applicationSettings = Configuration.GetSection("Settings").Get<ApplicationSettings>();
services.AddSingleton(applicationSettings);
// ** THIS IS WHAT I ORIGINALLY HAD, BUT IT'S ONLY SETTING
// THE VALUE IN DI/IOC.
//services.Configure<ApplicationSettings>(options => Configuration.GetSection("Settings")
// .Bind(options));
var foo = new Foo(applicationSettings.SomeSetting);
services.AddSingleton(foo);
}
See how i'm manually adding a singleton and then later, referring a value from the app settings instance?
vs
just configuring ...
So, is either way OK or is there a specific reason for either/or?
Remember -> i'm going to need to inject my settings into controllers, etc...
Technically, you can do either. In both cases, you have the configuration registered and available through dependency injection, so everything can depend on it and will get the configuration instance.
You are also using the centrally set up Configuration there, so you have all the benefits from the configuration stack there, e.g. multiple providers or environment specific overrides.
However, the favor has definitely moved to the IOptions way of consuming custom configuration. It’s the “state of the art” and used throughout ASP.NET Core for literally everything. It also allows you to switch to options that can be updated at runtime. That’s very powerful and might become useful eventually (not necessarily for your specific situation with the singleton, but maybe for something else).
It’s also really easy to set this up, actually shorter than what you tried:
services.Configure<ApplicationSettings>(Configuration.GetSection("Settings"));
services.AddSingleton<Foo>();
Note that, even for singletons, you shouldn’t explicitly create a new instance of it, but let DI handle that. If your class has the correct constructor, dependencies will be automatically injected anyway:
public class Foo
{
private readonly ApplicationSettings _settings;
public Foo (IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
}
Of course, Foo can also have more dependencies here. Since it’s going to be constructed by DI, you can just add more dependencies in the constructor, without having to update some new call somewhere.
If you need to configure certain services with settings that depend on your configuration, you still should not bind your configuration there directly. All of configuration is DI-based, so you just need to inject the right thing; a IConfigureOptions<T>. That’s basically the thing that provides the IOptions<T> to services later. In your JWT case, this could look like this:
// instead of passing an option configuration delegate here…
services.AddAuthentication().AddJwtBearer();
// … we register a IConfigureOptions<JwtBearerOptions> instead
services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();
// … ConfigureJwtBearerOptions could look like this:
class ConfigureJwtBearerOptions : IConfigureOptions<JwtBearerOptions>
{
private readonly ApplicationSettings _settings;
public ConfigureJwtBearerOptions(IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
public void Configure(JwtBearerOptions options)
{
// configure JwtBearerOptions here, and use your ApplicationSettings
options.MetadataAddress = _settings.JwtMetadataAddress;
}
}
This might seem unnecessarily verbose compared to just passing a delegate to AddJwtBearer() but note that this is exactly what happens under the hood when you pass that delegate: An IConfigureOptions<JwtBearerOptions> object will be created that calls your delegate in the Configure() call. So this is really just the same.
Note that for authentication schemes, you might actually set up a IConfigureNamedOptions<T> instead, which is almost the same thing except it can configure the options based on a name. For authentication schemes, that is the scheme name, so basically you check the scheme name in Configure() and then decide how to configure your options.
As for creating singleton instances, especially expensive ones, I would argue that ConfigureServices is the wrong place for such a thing. ConfigureServices is called very early in the application startup phase, when the whole DI infrastructure does not exist yet. So you could not rely on anything when creating your instance. I would also argue that it is still not your job to create the object but you should DI handle the creation of it and as such give it also control over its lifecycle.
If you absolutely need to control when the instance is created, I would suggest you to use the lifecycle events for this: Basically, after the application has set up properly but before a first request comes in, you request the instance of your services and initialize it. That way, you can still have it fully depend on DI, and it won’t be created lazily with the first request.
You can register lifecycle handlers in the Configure method:
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStarted.Register(() =>
{
// application has started, request the singleton here to trigger DI to
// create the instance
app.ApplicationServices.GetService<ExpensiveSingleton>();
});
// …
}
}
Well the problem with that approach is that it will be impossible to load multiple configuration sections through DI. The Configuration API has many features, such as pluggable configuration provides, snapshots, etc.
I would suggest you at least use a class to bind you configuration section against, so DI can inject it based on its type. If you further down the line have need to another configuration class you won't run into issues.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration