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)
Related
Only in the master branch in our project, a specific service injected in Startup does not resolve when calling a specific controller.
The injected "Service" is just a class with statics filled with configuration values.
In Startup.cs:
var azureAdConfiguration = Configuration.GetSection("AzureAD").Get<AzureAdConfiguration>();
services.AddSingleton<IAzureAdConfiguration>(azureAdConfiguration);
In the specific controller:
private readonly IAzureAdConfiguration _azureAdConfiguration;
public Controller(IConfiguration configuration, IAzureAdConfiguration azureAdConfiguration)
{
_configuration = configuration;
_azureAdConfiguration = azureAdConfiguration;
}
I have debugged startup, and the azureAdConfiguration object is properly filled from configuration.
What am i missing here? Why won't this resolve?
I'm new with .Net Core (using 3.1) and using Dependency injection. I was able to setup NLog in the Web API Controller but now I'm trying to get NLog to work in a separate business class following the basics of what I did in the API Controller. I keep get errors about the logger being NULL and when I put a break point on the _logger and _config, sure enough they are NULL. I'm not sure what I'm missing here.
This is my Business class and I thought I had it setup correctly but obviously not.
public class ShiftBLL
{
private static IConfiguration _config;
private static ILogger<ShiftBLL> _logger;
public ShiftBLL(ILogger<ShiftBLL> logger, IConfiguration config)
{
_config = config;
_logger = logger;
}
public static List<AppsShift> GetShifts(string station, string shiftDate)
{
_logger.LogInformation("Staion: {0} | ShiftDate: {1}", station, shiftDate);
*code removed for clarity. The app breaks on the initial call of _logger.
}
}
FIX
I removed the "static" from the ShiftBLL class as well as from the local parameters. Then I had to create an object of ShiftBLL in my Controller passing in the logger and config from the controller where I have DI working into the ShiftBLL. I ended up with this in my Controller:
ShiftBLL BLL = new ShiftBLL(_logger, _config);
listShifts = BLL.GetShifts(station, shiftDate);
Here is my updated ShiftBLL:
public class ShiftBLL
{
private IConfiguration _config;
private readonly ILogger _logger;
public ShiftBLL(ILogger logger, IConfiguration config)
{
_config = config;
_logger = logger;
}
public List<AppsShift> GetShifts(string station, string shiftDate)
{
_logger.LogInformation("Staion: {0} | ShiftDate: {1}", station, shiftDate);
}
Still getting my head wrapped around Dependency Injection.
When using DI, the DI container will insert the parameters for all dependencies when needed.
I will try to explain with a simplified example:
public class MyClass
{
public MyClass(ShiftBLL shiftBll)
{ .. }
}
When resolving MyClass, the DI container will do:
Find all needed dependencies for creating an instance of MyClass - in this case: ShiftBLL
Find all needed dependencies for creating ShiftBLL, in this ILogger and IConfiguration
Find the registrations for ILogger and IConfiguration (as those are interfaces), and dependencies etc.
Create a instance of ILogger and IConfiguration (via the registrations), create instance of ShiftBLL by injecting the instances into the constructor
Create a instance of MyClass and inject in instance of ShiftBLL.
So with this chain, all your constructor dependencies are created.
But if you're are using static methods in ShiftBLL, you are not sure if the constructor of ShiftBLL (with the ILogger) is ever invoked - so the ILogger field could be null. .
So to fix this case you could do:
Make the method non-static, or
Send all dependencies (ILogger) to the static method as parameter
You mixed up static methods calls and Dependency Injection. I would say to avoid the use of static methods whenever possible, anyway when you are using static methods you cannot access dependencies that are injected through dependency injection. For instance you cannot access _logger from GetShifts because you don't have an instance of ShiftBll and the fact that you call it statically means that its constructor is never called.
This is a working example of how to design classes and their dependencies and how to register them.
public class AController : Controller {
private readonly ILogger<AController> _logger;
private readonly ShiftBll _shiftBll;
public AController(ILogger<AController> logger, ShiftBll shiftBll) {
_logger = logger;
_shiftBll = shiftBll;
//I'm pretty sure you didn't inject the dependency here
//you preferred to use a static reference, but it is not correct in this case
}
public ActionResult AnAction() {
var shifts = _shiftBll.GetShifts("aStation", "aShiftDate");
}
}
public class ShiftBLL
{
private readonly IConfiguration _config; //not static
private readonly ILogger<ShiftBLL> _logger; //not static
public ShiftBLL(ILogger<ShiftBLL> logger, IConfiguration config)
{
_config = config;
_logger = logger;
}
public List<AppsShift> GetShifts(string station, string shiftDate) //not static
{
_logger.LogInformation("Staion: {0} | ShiftDate: {1}", station, shiftDate);
*code removed for clarity. The app breaks on the initial call of _logger.
}
}
.NET Core DI automatically registers controllers, but you have to manually register your own services. To do this go in the ConfigureServices method of your Startup.cs and add
services.AddScoped<ShiftBll>();
now you can use ShiftBll from AController.
System.InvalidOperationException: The current type, Microsoft.Extensions.Configuration.IConfiguration, is an interface and cannot be constructed. Are you missing a type mapping?
I am using unity with .NET Core, and the error above is being given at this line:
container.RegisterInstance<IServiceA>(IoC.Resolve<IServiceB>());
IServiceB is registered as such:
container.RegisterType<IServiceB, ServiceA>(new ContainerControlledLifetimeManager());
I have a class ServiceA that implements IService B as follows.
public class ServiceA : IServiceB
{
private IConfiguration _configuration;
public ServiceA(IConfiguration configuration): base(configReader)
{
_configuration = configuration;
}
public string someVariable => _configuration.GetSection("Section").GetSection("WantedKey").Value;
...
}
I am using Microsoft.Extensions.Configuration.IConfiguration for the Configuration.
So I do not know how to map it because I am getting it from Microsoft.Extensions.Configuration.IConfiguration.
What am I doing wrong? How can I solve it?
Assuming a file like config.json
{
"Section" : {
"WantedKey" : "WantedValue"
}
}
You would first need to build a configuration instance
IConfiguration config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json", optional: true, reloadOnChange: true);
.Build();
And register that with the container
container.RegisterInstance<IConfiguration>(config);
In order for IConfiguration to be available to be resolved.
Now ideally you really shouldn't be passing IConfiguration around for injection. It is usually accessed at the composition root to get settings for other dependencies. Try to avoid coupling to external framework dependencies.
In this case you would want to create a strong type to hold the desired settings
public class MySetting {
public string WantedKey { get; set; }
}
And populate an instance of it from configuration
MySetting setting = config.GetSection("Section").Get<MySetting>();
You would register that with the container
container.RegisterInstance<MySetting>(setting);
and have that injected into dependents
public class ServiceA : IServiceB {
private readonly MySetting setting;
public ServiceA(MySetting setting) {
this.setting = setting;
}
public string someVariable => setting.WantedKey;
//...
}
Reference Configuration in ASP.NET Core
I am doing some dependency injection with Microsoft.Practices.Unity.
For some classes, I am using injection factories like this:
container.RegisterType<ICar>(new InjectionFactory(o => {return new Car("Toyota")}));
Later in my code, I want to be able to find out if I have used or not an injection factory for a given interface.
I see that I can get regitrations in container.Registrations, but these objects do not give me injection members.
A possible way to get them would be to implement a wrapper around my IUnityContainer, that records the injection members.
But maybe there is some better way that directly leverages unity API ? Is there a way to get these injection members directly from the unity container ?
as suggested in my comment (but not with unity). Just copied it from my project.
public void ConfigureServices(IServiceCollection services)
{
var lgr = LogManager.GetCurrentClassLogger();
var logger = new NlogLogger(lgr);
services.AddSingleton<ILogger>(provider => logger);
services.AddSingleton<IMachineConfigFactory, MachineConfigFactory>();
services.AddSingleton<IMemoryCacheService, MemoryCacheService>();
services.AddSingleton<IServerManagerService, ServerManagerService>();
services.AddSingleton<ISubscriberServerHubService, SubscriberServerHubService>();
services.AddSingleton<IPurgeService, PurgeService>();
var configuration = GetConfiguration(option);
services.AddSingleton(configuration);
services.AddOptions();
services.Configure<HostConfig>(configuration.GetSection("HostConfig"));
services.AddSingleton<ServerManager>();
services.AddSingleton<CacheManagerService>();
return services;
}
and then:
public class HealthChecker : IHealthChecker
{
private readonly HealthCheckerConfig _config;
private readonly IAssetProvider _assetProvider;
public HealthChecker(IOptions<HealthCheckerConfig> config, IAssetProvider assetProvider)
{
_config = config.Value;
_assetProvider = assetProvider;
}
}
or am i missing something?
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");