Dependency injection : static parameters and services - c#

I have this service that will be injected to some other controllers.
It needs a service, and a connection string that is taken from a configuration file.
public class MyService : IMyService
{
public MyService(IService1 service1, IService2 service2, string connectionString){
//...
}
}
I would like IService1 and IService2 to be injected, but connectionString to be specified manually. I can't get my head around a way to work this out, the examples I saw were either massively complex, or just not what I wanted to achieve.
public void ConfigureServices(IServiceCollection services)
{
var cfg = new MyConfiguration();
Configuration.Bind("config", cfg);
var connectionString = cfg["myConnectionString"];
services.AddSingleton<IMyService, MyService>(/*what can I do here?*/)
}
Can this be achieved simply ?

You have access to the service provider within the factory delegate.
Resolve the other dependencies and inject the static variable when initializing the service.
//...
var connectionString = cfg["myConnectionString"];
services.AddSingleton<IMyService>(_ =>
new MyService(_.GetService<IService1>(), _.GetService<IService2>(), connectionString));
In the above example _ in the factory delegate is a IServiceProvider, and GetService<T> extension method is used to resolve the other services provided they are also registered with the service collection.
The factory delegate will be invoked the first time IMyService is requested.
As an alternative, referencing Options pattern in ASP.NET Core
And assuming for example a settings.json file
{
"myConnectionString": "value1_from_json",
}
consider using the IOptions<T> provided by the configuration extension.
create a class to store the desired configuration.
public class MyConnections {
public string MyConnectionString { get; set; }
}
refactor the class to depend on IOption<MyConnections>
public class MyService : IMyService {
private string connectionString;
public MyService(IService1 service1, IService2 service2, IOptions<MyConnections> options){
connectionString = options.Value.MyConnectionString;
//...
}
//...
}
and configure it on start up
//...
services.Configure<MyConnections>(Configuration);
services.AddSingleton<IMyService, MyService>();

You could do it like Nkosi suggests and use a factory that creates the service. This however has the drawback that this is very explicit and will require you to always adjust the object creation whenever your constructor signature changes (for example when you need different dependencies).
A more proper solution in the ASP.NET Core world would be to use the options pattern. What you do is basically instead of requiring a connection string to be passed to the constructor, you create a new configuration type that configures your service. You can then configure this object using the options pattern.
This would look like this:
public class MyService : IMyService
{
public MyService(IService1 service1, IService2 service2, IOptions<MyServiceOptions> serviceOptions)
{
var connectionString = serviceOptions.Value.ConnectionString;
//...
}
}
public class MyServiceOptions
{
public string ConnectionString
{ get; set; }
}
And then in your startup, just register your type and configure the options:
services.AddSingleton<IMyService, MyService>();
services.Configure<MyServiceOptions>(options => {
options.ConnectionString = "connection string";
});
If you place the configuration for your connection string into a separate configuration section, you could even do this:
services.Configure<MyServiceOption>(configuration.GetSection("MyService"));
Assuming your configuration looks like this:
{
"MyService": {
"ConnectionString": "…"
},
// …
}

Related

Is there a way to modify an IOptionsSnapshot<T> programmatically instead of a change in the source file?

I have a transient class registered in ASP.NET Core's DI.
An IOptions<T> is injected into its constructor. For every request and when needed, during runtime and based on a condition, I want to have another instance of IOptions injected.
Since IOptionsSnapshot<T> gets updated for every request when the source file gets updated, is there a way to mimic this behavior but instead of a change in the file, I want to programmatically make a change in IOptions, and before constructor injection, during runtime when a request comes in?
And use IOptionsSnapshot<T> instead of IOptions<T> for that.
Update:
Condition example to be run before injection happens somewhere in the app like maybe a controller or action custom attribute? In the attribute check a value and so:
if (some condition)
Options.cnnectionstring = "string1";
else
Options.cnnectionstring = "string2";
Injected into a class like this:
public class Books
{
private readonly string connectionString;
public Books(IOptions<DBOptions> options)
{
this.connectionString = options.Value.connectionString;
}
public void SomeMethod()
{
.... //uses connectionString
}
}
Registered like this:
services.Configure<DBOptions>(options =>
{
options.connectionString = "some connection string";
});
IOption (like IConfiguration) is registered as singleton, but the request is scoped. Then it isn't possible to use request's information to modify the configuration.
You can use a intermediate scoped service, that retrieve the request's information and generate the desired connection string, like :
public class BooksConnectionString
{
public IConfiguration _configuration;
public IHttpContextAccessor _httpContextAccessor;
public BooksConnectionString(IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
{
_configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
public string ConnectionString
{
get
{
var library = _httpContextAccessor.HttpContext.Request.Query["library"].First();
return _configuration.GetConnectionString(library);
}
}
}
Register the service as scoped :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
services.AddScoped<BooksConnectionString>();
}
...
}
Then you can inject like :
public class Books
{
private readonly string connectionString;
public Books(BooksConnectionString options)
{
this.connectionString = BooksConnectionString.ConnectionString;
}
}
It's suppose you can modify the class where the connection string is injected. If you can't modify this class, then you can register IOption as scoped :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
services.AddScoped<IOptions<DBOptions>>(p =>
{
var configuration = p.GetService<IConfiguration>();
var httpContextAccessor = p.GetService<IHttpContextAccessor>();
var library = httpContextAccessor.HttpContext.Request.Query["library"].First();
var dbOptions = configuration.GetSection("Databases").GetSection(library).Get<DBOptions>();
return Options.Create(dbOptions);
});
}
...
}
Warning, IOption are expected as singleton. Register IOption as scoped would break this expectation. To be used as a last resort.

RegisterSingleton with constructor parameter

I have a service called CustomerService, in a separate DLL project, and this service needs a connection string from App.config in the main project. This is the CustomerService:
public class CustomerService : ICustomerService
{
private string _mySQLConnectionString;
public CustomerService(string mySQLConnectionString)
{
_mySQLConnectionString = mySQLConnectionString;
}
public List<Customer> GetCustomerList()
{
return new List<Customer>();
}
}
I want to register this service with containerRegistry.RegisterSingleton as follows. How can I add the constructor parameter? Is there any other solution to share the connection string from the App.config with multiple services in separate DLL projects.
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<ICustomerService, CustomerService>();
}
Note: Connection string needed for multiple services to connect a MySQL server.
Question part two: How can I register another singleton call it "service" (example: SocketService) and share that singleton with other registered singletons, for example my CustomerService or ItemService? So all registered "services" can call the Connect() method from the SocketService.
You can create an instance of the type first and call RegisterInstance.
var mySQLConnectionString = "...";
var customerService = new CustomerService(mySQLConnectionString);
containerRegistry.RegisterInstance(customerService);
An alternative is to use a factory method instead, provided on an overload of RegisterSingleton.
var mySQLConnectionString = "...";
containerRegistry.RegisterSingleton<CustomerService>(() => new CustomerService(mySQLConnectionString));
If you need even more flexibility, e.g. resolving other types, use another overload with the container.
containerRegistry.RegisterSingleton<AnyService>(containerProvider =>
{
var anyOtherService = containerProvider.Resolve<AnyOtherService>();
// ...other complex setups
return new AnyService(anyOtherService);
});
How can I register another singleton call it "service" (example: SocketService) and share that singleton for other registered singletons for example my CustomerService or ItemService? So all registered "service" can call the my Connect() method from the SocketService.
Specify the dependencies as constructor parameters in your CustomerService (ideally with interfaces to ease unit testing) and register it and its dependencies to the container.
public class CustomerService : ICustomerService
{
private string _mySQLConnectionString;
public CustomerService(string mySQLConnectionString, ISocketService socketService)
{
_mySQLConnectionString = mySQLConnectionString;
// ...other code.
}
// ...other code.
}
var mySQLConnectionString = "...";
containerRegistry.RegisterSingleton<ISocketService, SocketService>();
containerRegistry.RegisterSingleton<CustomerService>(containerProvider =>
{
var sockertService = containerProvider.Resolve<SocketService>();
// ...other complex setups
return new CustomerService(mySQLConnectionString, sockertService);
});
Once you resolve any of these types, the container will take care of setting up the dependencies.

Retrieve Service and use it in some arbitrary class in net core

There are plenty of examples how to set controllers to use services etc. But what about plain old class? Lets use some simple configuration service
JSON
{
....,
"AppSettings": {
"SchemaFile": "some file name.xml"
}
}
POCO
public class AppSettings
{
public string SchemaFile { get;set; }
}
In startup.cs
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
. . . .
}
This is the point where all examples move directly to the controller. But we are going to have plenty of code outside controller. What I need is to access this service using provider.GetService(typeof(T)) or provider.GetRequiredService(typeof(T)), from, lets say a static class
internal static MyClass
{
internal static void DosomeThing()
{
// acquire my service
// use it to retrieve some value
// continue with my logic
}
}
Thanks
Just as the services can be injected into controllers, so too can they be injected into other classes.
static classes however to not lend themselves well to dependency injection by default.
Instead of using a static class, make a regular class and explicitly depend on the desired service via constructor injection
internal class MyClass : IMyService {
readonly AppSettings settings;
public MyClass(AppSettings settings) {
this.settings = settings;
}
internal void DosomeThing() {
// retrieve some value from settings
// continue with my logic
}
}
You can then register your desired POCO and utilities with the service container
public void ConfigureServices(IServiceCollection services) {
AppSettings appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>();
services.AddSingleton(appSettings);
services.AddSingleton<IMyService, MyClass>();
//. . . .
}
Inject your service where it is needed and it will have access to the POCO when being resolved for injection.
There really is no need to be passing IServiceProvider around as that can be seen as a code smell.
Simplifying your design to follow explicit dependency principle should make your code more SOLID and easier to follow and maintain.
You should pass AppSettings as parameter from the caller method
public class HomeController : Controller
{
public HomeController(AppSettings settings)
{
this.Settings = settings;
}
private AppSettings Settings { get; }
public IActionResult Index()
{
MyClass.DosomeThing(this.Settings);
}
}
internal static MyClass
{
internal static void DosomeThing(AppSettings settings)
{
// acquire my service
// use it to retrieve some value
// continue with my logic
}
}

How to inject connection string into external assembly (project) controller?

My Web API is using other project for one controller. Service works fine. But I am struggling to inject connection string from main Web API project into controller in external project.
How could this be achieved?
public class MyExternalController : Controller
{
private string _connStr;
public MyExternalController(string connStr)
{
_connStr = connStr;
}
// actions here
}
As others said in the comments, for something like a controller, you should be injecting something concrete like a DbContext, not a connection string. However, for future reference your issue here is injecting a string. There's no way to register something in the DI container to satisfy a dependency like that. Instead, you should inject your configuration or a strongly-typed configuration class.
Injecting IConfigurationRoot is a bit of an anti-pattern, but for something like a connection string, it's fine:
public MyExternalController(IConfigurationRoot config)
{
_connStr = config.GetConnectionString("MyConnectionString");
}
For everything else, though, you should use strongly-typed configuration classes.
public class FooConfig
{
public string Bar { get; set; }
}
Then, in ConfigureServices:
services.Configure<FooConfig>(Configuration.GetSection("Foo"));
Which of course would correspond with some bit of config like:
{
"Foo": {
"Bar": "Baz"
}
}
Then, in your controller, for example:
public MyExternalController(IOptionsSnapshot<FooConfig> fooConfig)
{
_fooConfig = fooConfig.Value;
}

How do you inject an arbitrary/changing set of services into an object using Autofac?

Using Autofac, given multiple interfaces in constructor parameters which is not what I want to achieve, let's say I have;
public class SomeController : ApiController
{
private readonly IDomainService _domainService;
private readonly IService1 _service1;
private readonly IService2 _service2;
private readonly IService3 _service3;
public SomeController(IDomainService domainService,
Iservice1 service1,
IService2 service2,
IService2 service3, ...)
{
_domainService = domainService;
_service1 = service1;
_service2 = service2;
_service3 = service3;
...
}
}
Or, we may do one interface and has multiple properties, e.g.;
public interface IAllServices
{
IDomainService DomainService { get; set; }
IService1 Service1 { get; set; }
IService2 Service2 { get; set; }
IService3 Service3 { get; set; }
}
public class SomeController : ApiController
{
private readonly IAllServices _allServices;
public SomeController(IAllServices allServices)
{
_allServices = allServices;
var domainService1 = _allServices.DomainService;
var service1 = _allServices.Service1;
etc...
}
}
However, I would like to have a list of services, and this code works for me, i.e.;
public interface IMyApp
{
IEnumerable<dynamic> Services { get; set; }
}
public class SomeController : ApiController
{
private readonly IMyApp _myapp;
public SomeController(IMyApp myapp)
{
_myapp = myapp;
foreach (var item in _myapp.Services)
{
if (item is IService1) { // do something... }
if (item is IService2) { // do something... }
if (item is IWhatever) { // do whatever something... }
}
}
}
But, I don't have a better best practice how to create the module, here is my module;
public class MainModule : Autofac.Module
{
private readonly string[] _serviceNames;
private readonly IDomainService _domainService;
public MainModule(IDomainService domainService, params string[] serviceNames)
{
_serviceNames = serviceNames;
_domainService = domainService;
}
protected override void Load(ContainerBuilder builder)
{
List<dynamic> _services = new List<dynamic>();
_services.Add(_domainService);
foreach (var serviceName in _serviceNames)
{
switch (serviceName)
{
case "MyService1":
IService1 service1 = new Service1();
_modules.Add(service1);
break;
case "MyService2":
IService2 service2 = new Service2();
_modules.Add(service2);
break;
case "SomeWhateverService":
IWhatever whateverService = new WhateverService();
_modules.Add(whateverService);
break;
}
}
builder.RegisterType<MyApp>()
.As<IMyApp>()
.WithParameter(new TypedParameter(typeof(IEnumerable<dynamic>), _services));
}
}
So, this code works, but I would like to make my DomainService and all of the Services registered in the container as well. That is, I want to replace whatever inside the switch statement without new keyword.
IService1 service1 = new Service1();
_modules.Add(service1);
And I would like to register the domain service as well. So, inside my Bootstrapper is like this;
public static class Initializer
{
public static IContainer BuildContainer(
HttpConfiguration config, Assembly assembly, IDomainService domainService, params string[] services)
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(assembly);
builder.RegisterWebApiFilterProvider(config);
builder.RegisterModule(new MainModule(domainService, services));
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
return container;
}
}
And what happen is, I need to create the domain service in the startup, i.e.;
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
MyDomainService domainService = new MyDomainService();
var container =
Initializer.BuildContainer(
GlobalConfiguration.Configuration,
Assembly.GetExecutingAssembly(),
domainService,
"MyService1", "MyService2", "SomeWhateverService");
}
}
You can see that I have to create the domain service first, which is not using IoC;
MyDomainService domainService = new MyDomainService();
and add to the module.
The big question, how to do this in proper way using Autofac. My Bootstrapper is in another project and all of the interfaces are in other project as well.
Many thanks for the help. And sorry for the long question.
Solution:
After testing several model, it seems the best way is to use domain events model for this type of scenario instead of injecting the services into the domain.
The proper way of doing dependency injection is using Constructor Injection. Constructor Injection should always your preferred choice, and only under high exception, you should fall back to another method.
You proposed property injection as an alternative, but this causes Temporal Coupling which means that classes can be initialized while a required dependency is missing, causing null reference exceptions later on.
The method where you inject a collection containing all services where the constructor is responsible of getting the dependencies it needs, is a variation of the Service Locator pattern. This pattern is littered with problems and is considered to be an anti-pattern.
Grouping dependencies into a new class and injecting that is only useful in case that class encapsulates logic and hides the dependencies. This pattern is called Facade Service. Having one big service that exposes the dependencies for others to use can be considered a form of the Service Locator anti-pattern, especially when the number of services that this class exposes starts to grow. It will become the common go-to object for getting services. Once that happens, it exhibits the same downsides as the other form of Service Locator does.
Extracting dependencies into a different class while allowing the consumer to use those dependencies directly doesn't help in reducing complexity of the consumer. That consumer will keep the same amount of logic and the same number of dependencies.
The core problem here seems that your classes get too many dependencies. The great thing about constructor injection though is that it makes it very clear when classes have too many dependencies. Seeking other methods to get dependencies doesn't make the class less complex. Instead of trying other methods of injection, try the following:
Apply the Single Responsibility Principle. Classes should have one reason to change.
Try extracting logic with its dependencies out of the class into a Facade Service
Remove logic and dependencies that deals with cross-cutting concerns (such as logging and security checks) from the class and place them in infrastructure (such as decorators, interceptors or depending on your framework into handlers, middleware, message pipeline, etc).
After testing several model, it seems the best way is just use domain events pattern for this type of scenario instead of injecting the services into the domain.
I refer to Udi Dahan article on domain events:
http://udidahan.com/2009/06/14/domain-events-salvation/

Categories

Resources