Say we have such AppSettings.json
{
"Region": Europe,
"WeirdService": {
"JustField": "value"
}
}
Registering WeirdService settings in separate, singleton class (or using options pattern) is fine, just:
service.AddSingleton(configuration.GetSection("WeirdService").Get<WeirdService>();
And at this point it's fine. I don't know however how to deal cleanly with this top-level properties like Region in my example.
I know I can just inject IConfiguration and use config.GetValue<string>("Region") or just access configuration directly, but I wonder if there is some clean, better way without hardcoding this stuff in services.
Edit
I forgot to mention. Team I'm currently working with uses .NET Core 3.1 as it's current LTS release.
I think the easiest way would be to just create a class for the toplevel keys. In your case you could create something like AppConfig with the single property Region. Then you just register it without getting a config section using the Configuration object, the Configure methods asks for a Configuration interface anyway and not a ConfigurationSection.
AppConfig:
public class AppConfig
{
public string? Region { get; set; }
}
Registration:
public static IServiceCollection AddOptions(this IServiceCollection services, IConfiguration configuration)
{
return services.Configure<AppConfig>(configuration);
}
Usage:
public class ExampleConsumer
{
public ExampleConsumer(IOptions<AppConfig> appConfig) {}
}
You got two options
Don't have any top level fields
All top level fields would go one level in. Your configuration would look something like:
{
"App": {
"Region": "east-us-2",
"ShowMaintenancePrompt": false
},
// other options follow
}
The advantage of this approach is you can keep adding to "App" as your application grows, and continue to use the options pattern.
Gather top-level fields into a class, and register that with DI
For a configuration like:
{
"Region": "east-us-2"
}
Create a AppConfig class like:
internal class AppConfig
{
public string? Region { get; set; }
}
And register this class with the DI:
var toplevelConfig = new AppConfig {
Region = configuration.GetValue<string>("Region")
};
services.AddSingleton<AppConfig>(toplevelConfig);
You can now inject AppConfig anywhere you'd like.
The only minor downside to this is that you cannot use the options pattern anymore.
Avoid injecting IConfiguration directly.
Updated
Method 1 Preferred way
The preferred way to read related configuration values is using the options pattern.
For more detail about the options pattern check the link below.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0
"WeirdService": {
"JustField": "value"
}
Create the WeiredServiceOptions class.
public class WeiredServiceOptions
{
public const string WeiredService = "WeiredService";
public string JustField { get; set; }
}
An options class:
Must be non-abstract with a public parameterless constructor.
All public read-write properties of the type are bound.
Fields are not bound. In the preceding code, WeiredService is not bound. The Position property is used so the string WeiredService doesn't need to be hardcoded in the app when binding the class to a configuration provider.
Calls ConfigurationBinder.Bind to bind the WeiredServiceOptions class to the WeiredService section.
var weiredServiceOptions = new PositionOptions();
configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
An alternative approach when using the options pattern is to bind the WeiredService section and add it to the dependency injection service container. In the following code, WeiredServiceOptions is added to the service container with Configure and bound to the configuration
services.Configure<WeiredServiceOptions>(Configuration.GetSection(
WeiredServiceOptions.Position));
and then read the WeiredService Options.
private readonly WeiredServiceOptions _options;
public YourClassContructor(IOptions<WeiredServiceOptions> options)
{
_options = options
}
Console.WriteLine($"JustField: {_options.JustField}");
Method 2
service.AddSingleton(configuration.GetSection("WeirdService:JustField").value);
Method 3
service.AddSingleton(configuration["WeirdService:JustField"]);
Doing: services.AddSingleton(Configuration.GetSection("WeirdService").Get<WeirdService>());
will register WeirdService to the Ioc container without supporting Options pattern. Assuming this is what you are looking for. Here is what you could do:
Create a class with properties mathcing the top level configuration similar to the AppConfig class a couple of people have suggested in the answers
Register the AppConfig class with the Ioc as below:
services.AddSingleton(Configuration.Get<AppConfig>());
Note:
Doing Configuration.Get<AppConfig>() will bind matching properties on the AppConfig class with the corresponding values from appsettings.json
Feel free to skip properties for keys that you do not want to bind
The IConfiguration.Get<T> is an extension method defined in Microsoft.Extensions.Configuration.ConfigurationBinder just in case
You can bind section to a class as well, it allows clean usage without using much of the magic strings.
public class WeirdService{
public string JustField{ get; set;}
public string AnotherField{ get; set;}
}
In controller you can then define a field
private readonly WeirdService _weirdService = new WeirdService();
public UserController(IConfiguration configuration)
{
configuration.GetSection("WeirdService").Bind(_weirdService);
//_weirdService.JustField
//_weirdService.AnotherField
}
You can access Region field using
configuration.GetValue<string>("Region")
or other way is
public Startup(IConfiguration configuration, IWebHostEnvironment
webHostEnvironment)
{
Configuration = configuration;
environment = webHostEnvironment;
}
then you can just use
Configuration["Region"]
Related
How to inject Settings created from IOptions value to call some class method?
I have MailAppSettings class and MailSender: IMailSender. Both are in different .NET projects.
Values into MailAppSettings are loaded using IOptions pattern in .NET Core. Values are loaded from appsettings.json configuration. For DI we use AutoFac, so it looks like this:
serviceCollection.AddOptions();
serviceCollection.Configure<MailAppSettings>(config.GetSection("MailAppSettings"));
And later then we access mailSettings like this:
public class CustomerSender:ICustomerSender
{
private readonly MailSettings _mailSettings;
private readonly IMailSender _mailSender;
public MyCustomerSender(IOptions<MailSettings> mailSettings, IMailSender mailSender)
{
_mailSettings = mailSettings.Value;
_mailSender = mailSender;
}
public SomeCustomMethod1() {
// use of mailsettings
var recipient = mailSettings.Recipient1;
var body = BuildSomeBody();
var subject = mailSettings.Subject1;
// mail sending
_mailSender.Send(recipient, body, subject);
}
mailSender is the class that sits in separate namespace and is called from different places in different projects.
public class MailSender: IMailSender
{
private readonly IMailSenderSettings _mailSenderSettings;
public MailSender(IMailSenderSettings confg) ///!!! here we need to inject those settings
{
_mailSenderSettings= confg;
}
...
Here, you see that mailSender needs IMailSenderSettings to be injected.
MailAppSettings implements this interface (but also contains additional properties).
My question is- how to inject MailAppSettings into IMailSenderSettings?
If I just register type like this:
containerBuilder.RegisterType<MailAppSettings>()
.As<IMailSenderSettings>()
.SingleInstance();
it won't work, values of injected IMailSenderSettings would be null.
IMailSenderSettings, as well as, MailAppSettings contains recipient, smtp host, password, user, but MailAppSettings also contains additional properties not needed for IMailSenderSettings
You will need to create IMailSenderSettings derived class that depends on MailSettings
public class MyMailSenderSettings: IMailSenderSettings {
private readonly MailSettings mailSettings;
public MyMailSenderSettings(IOptions<MailSettings> options) {
this.mailSettings = options.Value;
}
public string SomeProperty => mailSettings.SomeMatchingProperty;
//... other members mapped from settings
}
so that the desired members can be mapped in the composition root.
From there it is only a matter of registering the implementation so that the DI container can handle the rest.
//...
serviceCollection.AddOptions();
serviceCollection.Configure<MailAppSettings>(config.GetSection("MailAppSettings"));
serviceCollection.AddSingleton<IMailSenderSettings, MyMailSenderSettings>()
//...
Ok, so I have an app that gets a pretty good amount of traffic. I have been working with the Microsoft Azure and Coding teams to resolve a problem with memory. They have seen the GB's of logs and how found that the Microsoft.Extensions.Configuration code is taking up a lion's share of the RAM when we are under heavy load.
In my API code I have a "base controller" that all of the other controllers inherit from. This allows me to share common methods and the like. In this base controller I have created a global variable:
public IConfigurationRoot _configuration { get; }
This is, I believe, the culprit... but I am not sure how to get rid of it. This _configuration variable allows me to get access to my appsettings.json environment variables. I am not sure how to get access to these in a different way.
For instance... in a GET call I need to know if we have caching on or not.
bool isCaching = bool.Parse(_configuration["Data:Cache"]);
One thought I had is to make the _configuration private to the BaseController and make methods inside of there to get the properties I need (i.e. caching) so that the other controllers don't have to pass around this _configuration object.
Not sure if make it private will do anything though....
I am not sure why you need to be parsing the same values over and over again, when you could just read the configuration file during Startup and reuse it:
public class MyConfiguration
{
public bool CachingEnabled { get; set; }
// more configuration data
}
public void ConfigureServices(IServiceCollection services)
{
// your existing configuration
var myConfiguration = new MyConfiguration
{
CachingEnabled = bool.Parse(Configuration["Data:Cache"]),
// other properties
}
// register the data as a singleton since it won't change
services.AddSingleton(myConfiguration);
}
public class MyController : Controller
{
private readonly MyConfiguration configuration;
public MyController(MyConfiguration config)
{
configuration = config;
}
}
I am using Simple Injector in my project to hook up all the required dependencies, but I cannot call container.Verify because it creates a Singleton instance for an http configuration before the actual first request
public interface IConfiguration { }
public class Configuration : IConfiguration
{
public Configuration()
{
var httpContext = HttpContext.Current;
var httpRequest = currentHttpContext.Request;
var httpRequestUrl = currentHttpRequest.Url;
this.UriScheme = currentHttpRequestUrl.Scheme;
this.UriHost = currentHttpRequestUrl.Host;
this.UriPort = currentHttpRequestUrl.Port;
}
public string UriScheme { get; private set; }
public string UriHost { get; private set; }
public int UriPort { get; private set; }
}
public class ServiceA
{
private readonly _configuration;
public ServiceA(IConfiguration configuration)
{
_configuration = configuration
}
}
public class ServiceB
{
private readonly _configuration;
public ServiceB(IConfiguration configuration)
{
_configuration = configuration
}
}
This is a basic example of the scenario. I currently have around 60 services all depending on IConfiguration
All the configuration needs to happen when the configuration class is created
This is what I do to register the instances in the container
var container = new Container();
//container.RegisterSingleton<IConfiguration, Configuration>();
var lazy = new Lazy<InstanceProducer>(() =>
Lifestyle.Singleton.CreateProducer(typeof(IConfiguration), typeof(Configuration), container));
container.Register<ServiceA>();
container.Register<ServiceB>();
container.Verify(); // Creates configuration class --> not desired
As per How can I skip verification of an object in the container
So the trick here is to trigger the creation of new InstanceProducer instances after the verification process
I know the workaround is using Lazy<T> and InstanceCreator but I cannot finish hooking my code correctly
EDIT
The Configuration class has no dependencies. The problem with Configuration is that it gets created as a Singleton on the container.Verify method call and at that time the currentHttpRequest.Url is not the actual url
I suppose I can move the configuration from the constructor to a method (e.g GetConfiguration) and do some refactoring but I am curious if delaying the instance creation can be achieved under the scenario of the question
As explained by Mark Seemann in this article, injection constructors should be simple and reliable. They shouldn't do anything that might cause it to fail. Calling HttpContext.Current inside the constructor makes it unreliable, since this is something that might fail.
Besides this, your Configuration component now depends upon runtime data (the HttpContext.Current is runtime data), which is a sin, as explained in this article.
The solution to your problem however is really simple and straightforward. Just change your Configuration class to the following:
public sealed class Configuration : IConfiguration
{
public string UriScheme => this.Url.Scheme;
public string UriHost => this.Url.Host;
public int UriPort => this.Url.Port;
private Uri Url => HttpContext.Current.Request.Url;
}
Not only does this simplify things, it also removes the anti-patterns that you were applying which caused you trouble. Your constructor is now so simple, that it isn't even there anymore (can't get any simpler than this). And the runtime data is now only requested (from HttpContext.Current) after the object graph is constructed. This allows the container to verify its configuration reliably.
To access App Keys in a class library, do we need to do the following code in every class library and class where we need to access a AppKey?
public static IConfigurationRoot Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
This is what I found in Microsoft docs, but this looks very redundant.
Startup class in a project as below
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework().AddEntityFrameworkSqlServer()
.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration["Data:MyDb:ConnectionString"]));
}
}
Then how should I inject this "IConfigurationRoot" in each class of a project. And do I have to repeat this Startup class in each class Library? Why is this not part of .NET Core Framework?
The recommended way is to use the options pattern, provided by Microsoft and used heavily in ASP.NET Core.
Basically you create a strong typed class and configure it in the Startup.cs class.
public class MySettings
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
and initialize it in the Startup class.
// load it directly from the appsettings.json "mysettings" section
services.Configure<MySettings>(Configuration.GetSection("mysettings"));
// do it manually
services.Configure<MySettings>(new MySettings
{
Value1 = "Some Value",
Value2 = Configuration["somevalue:from:appsettings"]
});
then inject these options everywhere you need it.
public class MyService : IMyService
{
private readonly MySettings settings;
public MyService(IOptions<MySettings> mysettings)
{
this.settings = mySettings.Value;
}
}
By the principle of Information Hiding in Object-Oriented Programming, most classes should not need to have access to your application configuration. Only your main application class should need to directly have access to this information. Your class libraries should expose properties and methods to alter their behavior based on whatever criteria their callers deem necessary, and your application should use its configuration to set the right properties.
For example, a DateBox shouldn't need to know how timezone information is stored in your application configuration file - all it needs to know is that it has a DateBox.TimeZone property that it can check at runtime to see what timezone it is in.
How can keep all the configuration file code out of my logic code using Settings (ApplicationSettingsBase) and Dependency Injection?
With configuration I mean a customer specific configuration file.
Do I really have to inject a configuration class everytime I need it or is there another pattern?
It would be great to get some sample code!
Samples:
Static Configuration:
public static class StaticConfiguration
{
public static bool ShouldApplySpecialLogic { get; set; }
public static string SupportedFileMask { get; set; }
}
public class ConsumerOfStaticConfiguration
{
public void Process()
{
if (StaticConfiguration.ShouldApplySpecialLogic)
{
var strings = StaticConfiguration.SupportedFileMask.Split(',');
foreach (var #string in strings)
{
}
}
}
}
Non static Configuration:
public interface IConfiguration
{
bool ShouldApplySpecialLogic { get; set; }
string SupportedFileMask { get; set; }
}
public class Configuration : IConfiguration
{
public bool ShouldApplySpecialLogic { get; set; }
public string SupportedFileMask { get; set; }
}
public class Consumer
{
private readonly IConfiguration _configuration;
public Consumer(IConfiguration configuration)
{
_configuration = configuration;
}
public void Process()
{
if (_configuration.ShouldApplySpecialLogic)
{
var strings = _configuration.SupportedFileMask.Split(',');
foreach (var #string in strings)
{
}
}
}
}
Static Context with non static configuration:
public static class Context
{
public static IConfiguration Configuration { get; set; }
}
public class ConsumerOfStaticContext
{
public void Process()
{
if (Context.Configuration.ShouldApplySpecialLogic)
{
var strings = Context.Configuration.SupportedFileMask.Split(',');
foreach (var #string in strings)
{
}
}
}
}
Configuration classes reduce cohension and increase coupling in the consumers. This is because there may be many settings that don't relate to the one or two needed by your class, yet in order to fulfill the dependency, your implementation of IConfiguration must supply values for all of the accessors, even the irrelevant ones.
It also couples your class to infrastructure knowledge: details like "these values are configured together" bleed out of the application configuration and into your classes, increasing the surface area affected by changes to unrelated systems.
The least complex, most flexible way to share configuration values is to use constructor injection of the values themselves, externalizing infrastructure concerns. However, in a comment on another answer, you indicate that you are scared of having a lot of constructor parameters, which is a valid concern.
The key point to recognize is that there is no difference between primitive and complex dependencies. Whether you depend on an integer or an interface, they are both things you don't know and must be told. From this perspective, IConfiguration makes as much sense as IDependencies. Large constructors indicate a class has too much responsibility regardless of whether the parameters are primitive or complex.
Consider treating int, string and bool like you would any other dependency. It will make your classes cleaner, more focused, more resistant to change, and easier to unit test.
The important part to realize is that configuration is only one among several sources of values that drive your application's behavior.
The second option (non-static configuration) is best because it enables you to completely decouple the consumer from the source of the configuration values. However, the interface isn't required, as configuration settings are normally best modeled as Value Objects.
If you still want to read the values from a configuration file, you can do that from the application's Composition Root. With StructureMap, it might looks something like this:
var config = (MyConfigurationSection)ConfigurationManager.GetSection("myConfig");
container.Configure(r => r
.For<Consumer>()
.Ctor<MyConfigurationSection>()
.Is(config));
One way is to inject a configuration interface like you post. Here are a couple other ways.
Exposing a Setter
class Consumer
{
public bool ShouldApplySpecialLogic { get; set; }
...
}
In the composition root, you can read a config file or hardcode it. Autofac example:
builder.RegisterType<Consumer>().AsSelf()
.OnActivated(e => e.Instance.ShouldApplySpecialLogic = true);
This is probably only advisable when you have a good default
Constructor Injection
public class Server
{
public Server(int portToListenOn) { ... }
}
In the composition root:
builder.Register(c => new Server(12345)).AsSelf();
In my applications I do what you have done above with IoC. That is to say, having my IoC container (StructureMap also) inject an IApplicationSettings into my classes.
For example, in an ASP.NET MVC3 project it may look like:
Public Class MyController
Inherits Controller
...
Private ReadOnly mApplicationSettings As IApplicationSettings
Public Sub New(..., applicationSettings As IApplicationSettings)
...
Me.mApplicationSettings = applicationSettings
End Sub
Public Function SomeAction(custId As Guid) As ActionResult
...
' Look up setting for custId
' If not found fall back on default like
viewModel.SomeProperty = Me.mApplicationSettings.SomeDefaultValue
Return View("...", viewModel)
End Function
End Class
My implementation of IApplicationSettings pulls most things from the app's .config file and has a few hard-coded values in there as well.
My example wasn't logic flow-control (like your example), but it would have worked just the same if it was.
The other way to do this would be to do a service-locator type pattern, where you ask your Dependency Injection container to get you an instance of the configuration class on-the-fly. Service-Location is considered an anti-pattern generally, but might still be of use to you.