I'd like to use AutoFac in a way that both a State and Strategy pattern coexist. After researching how, I got familiar with the Keyed/Named registration of Autofac and used this for my states using the passive IIndex method. After this, I was looking at the Strategy pattern, which to me looked like a good way of using the same idea, with resolving IIndex for both State and Strategy. I've saved my Strategy options in the same way (enum) as State and Keyed them in the DependencyResolver:
builder.RegisterType<NewAanvraag>().Keyed<IAanvraagState>(AanvraagState.Nieuw).Keyed<IAanvraagState>(BusinessState.Default);
builder.RegisterType<RareNewAanvraag>().Keyed<IAanvraagState>(AanvraagState.Nieuw).Keyed<IAanvraagState>(BusinessState.Rare);
builder.RegisterType<OpvoerenInformatie>().Keyed<IAanvraagState>(AanvraagState.OpvoerenInformatie).Keyed<IAanvraagState>(BusinessState.Default);
This way, I would like to use both options to be created in dynamic order, whereas some implementations might be the same as the default, and some are not.
However, when trying to access both the state and the strategy, I got a notion of KeyedServiceIndex2 (DelegateActivator), but neither option could be resolved by itself
private readonly IIndex<AanvraagState, IAanvraagState> _states;
private readonly IIndex<BusinessState, IAanvraagState> _strategyState;
public IAanvraagDto AanvraagDto { get; set; }
private IAanvraagState CurrentState{ get { return _states[AanvraagDto.State];} }
private IAanvraagState CurrentStrategy { get { return _strategyState[AanvraagDto.BusinessState]; } }
public Aanvraag(IIndex<AanvraagState, IAanvraagState> states, IIndex<BusinessState, IAanvraagState> strategyState)
{
_states = states;
_strategyState = strategyState;
}
public void Start()
{
CurrentStrategy.Start(AanvraagDto);
SetState(AanvraagState.OpvoerenInformatie);
}
When I tried to use both it couldn't find the implementation (also tried IIndex<BusinessState, IIndex<AanvraagState, IAanvraagState>>):
private readonly IIndex<AanvraagState, IIndex<BusinessState, IAanvraagState>> _states;
public IAanvraagDto AanvraagDto { get; set; }
private IAanvraagState CurrentState { get { return _states[AanvraagDto.State][AanvraagDto.BusinessState]; } }
public Aanvraag(IIndex<AanvraagState, IIndex<BusinessState, IAanvraagState>> states)
{
_states = states;
}
public void Start()
{
CurrentState.Start(AanvraagDto);
SetState(AanvraagState.OpvoerenInformatie);
}
Does anyone know how to use 2 Keyed variables to retrieve a grid-like structure for resolving the concrete implementation?
PS: This is the first question I ask on StackOverflow, so any constructive feedback is highly appreciated.
The IIndex<K,V> relationship is really just for single-dimension keyed services. It won't work for multi-dimensional selection.
What you're more likely looking for is component metadata, the ability to associate any arbitrary data with a registration and select the registration based on that data.
The documentation has some great examples and details, but I'll show you a simple example that might fit closely with what you're doing.
First, you need to define a metadata class. This is the thing that will track the various "dimensions" of the "matrix" by which you want to select your component. I'll do something simple here - two Boolean fields so there are only four total combinations of metadata available:
public class ServiceMetadata
{
public bool ApplicationState { get; set; }
public bool BusinessState { get; set; }
}
I'll use some very simple empty services just for illustration. Yours will obviously do something more. Note I have four services - one for each combination of metadata.
// Simple interface defining the "service."
public interface IService { }
// Four different services - one for each
// combination of application and business state
// (e.g., ApplicationState=true, BusinessState=false).
public class FirstService : IService { }
public class SecondService : IService { }
public class ThirdService : IService { }
public class FourthService : IService { }
Here's where you consume the services. To more easily take advantage of the strongly-typed metadata, you'll need to reference System.ComponentModel.Composition so you have access to System.Lazy<T, TMetadata>.
public class Consumer
{
private IEnumerable<Lazy<IService, ServiceMetadata>> _services;
public Consumer(IEnumerable<Lazy<IService, ServiceMetadata>> services)
{
this._services = services;
}
public void DoWork(bool applicationState, bool businessState)
{
// Select the service using LINQ against the metadata.
var service =
this._services
.First(s =>
s.Metadata.ApplicationState == applicationState &&
s.Metadata.BusinessState == businessState)
.Value;
// Do whatever work you need with the selected service.
Console.WriteLine("Service = {0}", service.GetType());
}
}
When you do your registrations, you'll need to register the metadata along with the components so they know which combination of data they belong to.
var builder = new ContainerBuilder();
builder.RegisterType<Consumer>();
builder.RegisterType<FirstService>()
.As<IService>()
.WithMetadata<ServiceMetadata>(m => {
m.For(sm => sm.ApplicationState, false);
m.For(sm => sm.BusinessState, false);
});
builder.RegisterType<SecondService>()
.As<IService>()
.WithMetadata<ServiceMetadata>(m => {
m.For(sm => sm.ApplicationState, false);
m.For(sm => sm.BusinessState, true);
});
builder.RegisterType<ThirdService>()
.As<IService>()
.WithMetadata<ServiceMetadata>(m => {
m.For(sm => sm.ApplicationState, true);
m.For(sm => sm.BusinessState, false);
});
builder.RegisterType<FourthService>()
.As<IService>()
.WithMetadata<ServiceMetadata>(m => {
m.For(sm => sm.ApplicationState, true);
m.For(sm => sm.BusinessState, true);
});
var container = builder.Build();
Finally, you can then use your consumer class to get services by "matrix," as you say. This code:
using(var scope = container.BeginLifetimeScope())
{
var consumer = scope.Resolve<Consumer>();
consumer.DoWork(false, false);
consumer.DoWork(false, true);
consumer.DoWork(true, false);
consumer.DoWork(true, true);
}
Will yield this on the console:
Service = FirstService
Service = SecondService
Service = ThirdService
Service = FourthService
Again, you'll definitely want to check out the documentation for additional details and examples. It will add clarification and help you understand other options you have available to maybe make this easier or work better in your system.
Related
Assuming this use case:
You've got two classes X and Y that depends on a configuration of type Config
public class X
{
public X(IOptions<Config> config)
{
}
}
public class Y
{
public Y(IOptions<Config> config)
{
}
}
Now, you want to create each an instance of X and Y, but with different configurations. What would be the right way to register this?
From everything I read, the only way to solve this would be by adding some sort of "naming" for the different configuration instances and resolve them via a custom resolver:
public delegate Config ServiceResolver(string key);
services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
switch (key)
{
case "A":
return ... (whatever to get the first config instance);
case "B":
return ... (whatever to get the second config instance);
default:
throw new KeyNotFoundException();
}
});
However, this means that the implementation of each X and Y must know about details about how to get the configurations:
They must know the correct name (A or B) and
they must know the ConfigResolver type, which is only an implementation detail/helper class for the sake of dependency injection.
This problem hits even harder if you need to go through several stages of dependencies, like
Config (A) Config (B)
| |
v v
Service Service
| |
v v
X Y
My feeling is, there should be a better way to solve this.
Like some form of receipent dependent service factory:
Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => {
services.Configure<Config>(context.Configuration.GetSection("ConfigA")).For<X>();
services.Configure<Config>(context.Configuration.GetSection("ConfigB")).For<Y>();
});
and maybe
Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => {
services.AddTransient<Service>((services, receiverType) => {
if(receiverType == typeof(X)) {
... resolve a service instance;
}
else {
... resolve some other service instance;
}
});
});
So, is there just some feature I missed until now? Is my understanding of the situation totaly misguided? Or is this really a feature that should be, but has not been added until now?
EDIT:
To make my point clearer: Just assume that X and Y are classes of a third-party library. Their constructors signature cannot be changed by you, as you don't have access to the source code.
So, how would you set this up in a way that you can get each an instance of X with ConfigA and an instance of Y with ConfigB?
Another EDIT 2023-01-02:
Happy new year everyone :)
Seems I have to describe a bit better what's my problem. This is not constrained to IOptions/configurations, but more a general question about where to decide about which service to inject and how it is configured.
Assume I have two a congress location with 2 stages. I call them "bigStage" and "smallStage", but in the end they've got the same implementation. I also got two speakers invited, called "loadSpeaker" and "quietSpeaker", but at this moment in time I don't know which one will speak on which of the two stages.
So I decide I've got this setup:
class Stage {
public Stage(string name, ISpeaker speaker) {
...
}
}
class Speaker: ISpeaker {
public Speaker(string name) {
...
}
}
Now, at the latest time possible, I want to compose my final setup so that I've got 2 Stages (called bigStage and smallStage) and their assigned Speakers (loudSpeaker on bigStage and quietSpeaker on smallStage). This composition/assignment should completely happen in my composition root, so that no code changes have to happen in the rest of my code. How can I do that?
I suggest to use a factory for your Service:
class X {
private readonly Service _service;
public X(ServiceFactory serviceFactory) {
_service = serviceFactory.Create<X>();
}
}
class Service {
private readonly Config _config;
public Service(Config config) { _config = config; }
}
class ServiceFactory {
private readonly IConfiguration _configuration;
/* other Service dependencies would also be injected here */
public ServiceFactory(IConfiguration configuration, /* Service dependencies */) {
_configuration = configuration;
...
}
public Service Create<T>() {
return Create(typeof(T));
}
public Service Create(Type type) {
var configName = switch typeof(T) {
X => "ConfigX",
Y => "ConfigY",
default => throw new Exception()
};
var config = _configuration.GetSection(configName).Get<Config>();
return new Service(config, /* other dependencies */);
}
}
The switch statement can be replaced with a Dictionary<Type, string> or Dictionary<string, string> if you would want to export this dictionary to IConfiguration.
Getting the Config can be also cached for performance (don't forget the thread safety)
So the "trick" to all of this is... you have to piggy back onto ~something to make a decision on which one IMySomething . when you register multiple IMySomething(s).
The factory above where you switch/case on the object.TYPE....is one way.
But it is "fragile", IMHO. Or at the very last, violates the Open/Closed principle of SOLID, as you have to keep editing the code to add a new case-statement.
So I also think you want a Factory.......BUT I do not like "hard coding" the values of the switch/case statements.
So if you follow my IShipper example:
Using a Strategy and Factory Pattern with Dependency Injection
I think you want to create a
IShipperFactory
and inject the IEnumerable of "IShipper"(s).
..
Then you will use your IShipperFactory... when registering your things that need an IShipper.
This does cause a small "ripple" because you need access to the IShipperFactory....to do (later) IoC registrations.
But it would be "clean" and have good separations of concerns.
Let me pseudo code it.
public interface IShipper (from other article)
3 concretes (Usps, FedEx, Ups)
public interface IShipperFactory()
public IShipper GetAnIShipper(string key)
..
public class ShipperFactoryConcrete
(from other article, inject multiple IShippers here)
public IShipper GetAnIShipper(string key)
// look through the injected IShippers to find a match, or else throw exception.
.....
public interface IOrderProcessor
..
public class WestCoastOrderProcessor : IOrderProcessor
/* c-stor */
public WestCoastOrderProcessor(IShipper aSingleShipper)
public class EastCoastOrderProcessor : IOrderProcessor
/* c-stor */
public WestCoastOrderProcessor(IShipper aSingleShipper)
........
Ok, so we decide at compile-time, we want to define the "best" IShipper for the EastCoastOrderProcessor and WestCoastOrderProcessor. (making up some kind of example here)
So need need to IoC register.
from the other article:
cont.RegisterType<IShipper, FedExShipper>(FedExShipper.FriendlyName);
cont.RegisterType<IShipper, UspsShipper>(UspsShipper.FriendlyName);
cont.RegisterType<IShipper, UpsShipper>(UpsShipper.FriendlyName);
now it gets a little "off beaten path".
See:
https://stackoverflow.com/a/53885374/214977
and
// so this is a cart-horse situation, where we need something from the IoC container.... to complete the IoC registrations.
IShipperFactory sf = services.GetRequiredService<IShipperFactory>(); // see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0#resolve-a-service-at-app-start-up
.. and now we IoC register...but we specify specific values for the constructor. please see the SOF (214977), for syntax-sugar hints. the below is definately pseduo code.....
_serviceCollection.AddSingleton<IOrderProcesor>(x =>
ActivatorUtilities.CreateInstance<EastCoastOrderProcessor>(x, sf.GetAnIShipper(FedExShipper.ShipperName));
);
_serviceCollection.AddSingleton<IOrderProcesor>(x =>
ActivatorUtilities.CreateInstance<WestCoastOrderProcessor>(x, sf.GetAnIShipper(UspsShipper.ShipperName));
);
APPEND:ONE:
Another "trick" .. if you have a code base that you cannot change is.
The "proxy design pattern":
The Proxy design pattern provides a surrogate or placeholder for
another object to control access to it.
https://www.dofactory.com/net/proxy-design-pattern
public EastCoastOrderProcessorProxy
private readonly ThirdPartyOrderProcessor innerThirdPartyOrderProcessor;
public EastCoastOrderProcessor(ThirdPartyOrderProcessor innerThirdPartyOrderProcessor)
{
this.innerThirdPartyOrderProcessor = innerThirdPartyOrderProcessor;
}
..
public WestCoastOrderProcessorProxy
private readonly ThirdPartyOrderProcessor innerThirdPartyOrderProcessor;
public EastCoastOrderProcessor(ThirdPartyOrderProcessor innerThirdPartyOrderProcessor)
{
this.innerThirdPartyOrderProcessor = innerThirdPartyOrderProcessor;
}
So while you cannot change the ThirdPartyOrderProcessor, you can write 1:N wrapper-proxies around it.
The simplest solution I can think of, without using named options inside of your service classes, is moving the selection of the configuration object from the class constructor to the composition root of the application.
This way, your service class simply receives a configuration object as a constructor parameter and it is not aware of the underlying configuration infrastructure.
The composition root, which is in charge of composing the objects which make your application, do know about the configuration infrastructure and picks the right configuration object for your services.
In order to implement this pattern, you need to define an option class as the first step. This option class is needed in order to leverage the options pattern support offered by ASP.NET core. You will only use this class at the composition root level.
public sealed class LayoutOptions
{
public const string Layout = "Layout";
public const string Basic = "Basic";
public const string Complex = "Complex";
public string Name { get; set; } = default!;
public string Color { get; set; } = default!;
public int NumberOfColumns { get; set; }
}
Then you need to define a class which represents the configuration object for your services. This is basically a strongly typed configuration object used to configure your services. This object is built strating from the options class, notice that you don't need to make it identical to the options class itself.
public sealed class LayoutConfiguration
{
public string Name { get; }
public string Color { get; }
public LayoutConfiguration(string name, string color)
{
Name = name;
Color = color;
}
}
Now you need to define your service classes. These types are configured by using the LayoutConfiguration configuration class. Each service class will be properly configured by the composition root of the application, by using the proper named options.
public interface ILayoutService
{
string GetLayoutDescription();
}
public sealed class BasicLayoutService : ILayoutService
{
private readonly LayoutConfiguration _config;
public BasicLayoutService(LayoutConfiguration config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
}
public string GetLayoutDescription() =>
$"Basic layout description. Name: '{_config.Name}' Color: '{_config.Color}'";
}
public sealed class ComplexLayoutService : ILayoutService
{
private readonly LayoutConfiguration _config;
public ComplexLayoutService(LayoutConfiguration config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
}
public string GetLayoutDescription() =>
$"Complex layout description. Name: '{_config.Name}' Color: '{_config.Color}'";
}
You can also defined a couple of controllers, that you can use to test this implementation and be user that your services are wired-up correctly by the composition root of the application:
[ApiController]
[Route("[controller]")]
public sealed class BasicLayoutController : ControllerBase
{
private readonly BasicLayoutService _basicLayoutService;
public BasicLayoutController(BasicLayoutService basicLayoutService)
{
_basicLayoutService = basicLayoutService ?? throw new ArgumentNullException(nameof(basicLayoutService));
}
[HttpGet("description")]
public string GetDescription() => _basicLayoutService.GetLayoutDescription();
}
[ApiController]
[Route("[controller]")]
public sealed class ComplexLayoutController : ControllerBase
{
private readonly ComplexLayoutService _complexLayoutService;
public ComplexLayoutController(ComplexLayoutService complexLayoutService)
{
_complexLayoutService = complexLayoutService ?? throw new ArgumentNullException(nameof(complexLayoutService));
}
[HttpGet("description")]
public string GetDescription() => _complexLayoutService.GetLayoutDescription();
}
This is the most important part. Put this registration code inside the Program.cs class (which is the composition root for an ASP.NET core 6 application):
// Configure named options
builder.Services.Configure<LayoutOptions>(
LayoutOptions.Basic,
builder.Configuration.GetSection($"{LayoutOptions.Layout}:{LayoutOptions.Basic}")
);
builder.Services.Configure<LayoutOptions>(
LayoutOptions.Complex,
builder.Configuration.GetSection($"{LayoutOptions.Layout}:{LayoutOptions.Complex}")
);
// Register the BasicLayoutService by picking the right configuration
builder
.Services
.AddScoped(serviceProvider =>
{
// Get named options
var layoutOptions = serviceProvider.GetRequiredService<IOptionsSnapshot<LayoutOptions>>();
var basicLayoutOptions = layoutOptions.Get(LayoutOptions.Basic);
// Create strongly typed configuration object from named options
var configuration = new LayoutConfiguration(
basicLayoutOptions.Name,
basicLayoutOptions.Color);
// Creates new instance of BasicLayoutService using the service provider and the configuration object
return ActivatorUtilities.CreateInstance<BasicLayoutService>(
serviceProvider,
configuration);
});
// Register the ComplexLayoutService by picking the right configuration
builder
.Services
.AddScoped(serviceProvider =>
{
// Get named options
var layoutOptions = serviceProvider.GetRequiredService<IOptionsSnapshot<LayoutOptions>>();
var complexLayoutOptions = layoutOptions.Get(LayoutOptions.Complex);
// Create strongly typed configuration object from named options
var configuration = new LayoutConfiguration(
complexLayoutOptions.Name,
complexLayoutOptions.Color);
// Creates new instance of ComplexLayoutService using the service provider and the configuration object
return ActivatorUtilities.CreateInstance<ComplexLayoutService>(
serviceProvider,
configuration);
});
You can now test this implementation. As an example, you can set the following configuration in appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Layout": {
"Basic": {
"Name": "Basic Layout",
"Color": "red",
"NumberOfColumns": 2
},
"Complex": {
"Name": "Complex Layout",
"Color": "blue",
"NumberOfColumns": 3
}
}
}
If you run this application and you issue a GET request to /BasicLayout/description, you ge the following response:
Basic layout description. Name: 'Basic Layout' Color: 'red'
If you issue a GET request to /ComplexLayout/description the response you get is:
Complex layout description. Name: 'Complex Layout' Color: 'blue'
A final note on the service lifetime for BasicLayoutService and ComplexLayoutService. In my example I decided to register them as scoped services, because you may want to recompute the configuration object for them (LayoutConfiguration) for each incoming request. This is useful if your configuration may change over time. If this is not the case, you can safely register them as singleton services. That's up to you and depends on your requirements.
I'm trying to handle a number of subscriptions in and Azure.Messaging.SericeBus.
The Docs suggest that I should register my ServiceBusClient, ServiceBusSender and ServiceBusProcessor for DI.
For the latter, it means I need an instance for each subscription, so I have something like this...
services.AddSingleton(provider =>
{
var options = provider.GetService<IOptions<WorkerOptions>>()
.Value;
var client = provider.GetService<ServiceBusClient>();
var subscription = // code to determine the subscription to use
return client.CreateProcessor(options.Topic, subscription);
}
Now I need to instantiate the processors and that's where I come unstuck. In this example I'm using an IServiceProvider but I think I'm going to have the same problem just using DI and constructor injection.
var processor = MyServiceProvider.GetService<ServiceBusProcessor>()!;
How do I get a specific ServiceBusProcessor?
I thought I should be able to "name" each instance but that doesn't appear to be possible.
What am I missing?
With .NET Core DI, you need to use separate types to discern between the injection targets. One way to do this is to create a dedicated class for each subscription, e.g.
public abstract class ProcessorProvider
{
private readonly ServiceBusProcessor _proc;
public ProcessorProvider(ServiceBusProcessor proc)
{
_proc = proc;
}
public virtual ServiceBusProcessor Processor { get => _proc; }
}
public class ProcessorProviderA : ProcessorProvider
{
public ProcessorProviderA(ServiceBusProcessor proc): base(proc) {}
}
public class ProcessorProviderB : ProcessorProvider
{
public ProcessorProviderB(ServiceBusProcessor proc): base(proc) {}
}
In your classes, you do not inject the processor directly, but rely on the different classes that provide the processor, e.g.
public class ClassThatReliesOnSubscriptionA
{
private readonly ServiceBusProcessor _proc;
public ClassThatReliesOnSubscriptionA(ProcessorProviderA procProv)
{
_proc = _procProv.Processor;
}
}
// Same for subscription B
This way, you can add a registration for IProcessorProviderForSubscriptionA and IProcessorProviderForSubscriptionB like this:
services.AddSingleton<ProcessorProviderA>(provider =>
{
var options = provider.GetService<IOptions<WorkerOptions>>()
.Value;
var client = provider.GetService<ServiceBusClient>();
var subscription = // Access subscription A
var proc = client.CreateProcessor(options.Topic, subscription);
return new ProcessorProviderA(proc);
}
services.AddSingleton<ProcessorProviderB>(provider =>
{
var options = provider.GetService<IOptions<WorkerOptions>>()
.Value;
var client = provider.GetService<ServiceBusClient>();
var subscription = // Access subscription B
var proc = client.CreateProcessor(options.Topic, subscription);
return new ProcessorProviderB(proc);
}
This way the inversion of control container can discern between the types that are required by the classes (ClassThatReliesOnSubscriptionA in this sample). Please note that above code is a sample that can give you an outline on how to solve the problem. You can optimize the code further, e.g. by moving common steps into ProcessorProvider. In order to improve "mockability" in unit tests, you could also use marker interfaces instead of the classes.
I have a small class to obtain a series of information about my user on several of my MVC applications. A minimal reproducible example would be:
public class InformationGetter
{
public string GetUserInformation(string connectionStr, string storedProcedureName, int userId)
{
// Do SQL work
return info;
}
}
I'm injecting it on the ConfigureServices step using
services.AddScoped<InformationGetter>
And then in my classes I simply call it from the DI.
Now, obviously the connectionStr and storedProcedure only changes per application but right now I'm passing it as parameter.
I've tried to make those parameters public and configure it using services.Configure but when I call it from my controllers, I get null values.
services.AddOptions();
services.Configure<InformationGetter>(options =>
{
options.ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
options.StoredProcedureName = "prInformationGetter";
});
I'm not sure if the reason why this is failing it's because I'm missing an interface on my original class or am I failing to understand this concept.
I've also thought on doing something like services.AddInformationGetter(options => {}) but my understanding is that this pattern is to implement middlewares and not DI specifically.
I tried checking the documentation (learn.microsoft.com) but I got even more confused.
There may be misunderstanding of the concepts involved.
Configure<TOption> will register IOptions<TOptions>. There are now two separate registrations in your example.
Once when you register the class
services.AddScoped<InformationGetter>()
and the other when you register the options.
Do the following
//..
services.AddOptions();
//Adds IOptions<InformationGetter>
services.Configure<InformationGetter>(options => {
options.ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
options.StoredProcedureName = "prInformationGetter";
});
//Adds InformationGetter but gets it from the registered options
services.AddScoped<InformationGetter>(sp =>
sp.GetRequiredService<IOptions<InformationGetter>>().Value
);
//...
The scoped registration will use the factory delegate to extract the options registered and return the desired type.
public class InformationGetter {
public string ConnectionString { get; set; }
public string StoredProcedureName { get; set; }
//...
public string GetUserInformation(int userId) {
// Do SQL work
return info;
}
}
InformationGetter looks like a service.
I would suggest refactoring to follow a more Single Responsibility Principle (SRP) and Separation of Concerns (Soc) design.
//Needed by InformationGetter to perform its function
public class InformationGetterOptions {
public string ConnectionString { get; set; }
public string StoredProcedureName { get; set; }
}
//abstraction of InformationGetter
public interface IInformationGetter {
string GetUserInformation(int userId);
}
//implementation.
public class InformationGetter : IInformationGetter{
private readonly InformationGetterOptions options;
public InformationGetter(InformationGetterOptions options) {
this.options = options;
}
public string GetUserInformation(int userId) {
//use values in options to connect
// Do SQL work
return info;
}
}
I would have avoid options pattern altogether and just registered the class using the delegate factory, extracting what I need from configuration. That way your code is not tightly coupled to framework concerns like IOptions
public void ConfigureServices(IServiceCollection services) {
//...
InformationGetterOptions options = new InformationGetterOptions {
ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
StoredProcedureName = "prInformationGetter";
};
services.AddSingleton(options);
services.AddScoped<IInformationGetter, InformationGetter>();
//...
}
Now IInformationGetter can be injected where needed and have all the necessary dependencies to perform its function.
I am facing a little problem related to dependency injection. I have a program, using respectfully state of the art rules of dependency injection, used from example of an ASP.NET MVC project, and some console programs.
But it also contains some kind of "service locator" anti pattern.
I'll attempt to illustrate it with a very simple console project :
using System;
using Autofac;
using System.Collections.Generic;
using System.Linq;
namespace AutofacIssue
{
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyModule>();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<UserService>();
var users = service.CreateSomeUsers();
var info = users.First().GetSomeInfo;
Console.WriteLine(info.Something);
}
}
}
internal class MyModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<UserService>();
builder.RegisterType<UserRelatedInfoService>();
}
}
internal class UserRelatedInfoService
{
public UserInfo GetForUser(int id)
{
return new UserInfo("Various infos for " + id);
}
}
internal class UserService
{
public IEnumerable<User> CreateSomeUsers()
{
return Enumerable.Range(1, 10).Select(r => new User(r)); // Remark "new User()" is called from many various spaces !
}
}
internal class UserInfo
{
// Some things
public string Something;
public UserInfo(string someThings)
{
this.Something = someThings;
}
}
/// <summary>
/// "Service locator" antipattern
/// </summary>
class DependencyLocator
{
public static IContainer Container { get; }
static DependencyLocator()
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyModule>();
Container = builder.Build();
}
}
internal class User
{
public User(int id)
{
this.Id = id;
}
public int Id { get; private set; }
/// <summary>
/// Here's my problematic property using the DependencyLocator
/// </summary>
public UserInfo GetSomeInfo
{
get
{
UserRelatedInfoService userRelatedInfoService = DependencyLocator.Container.Resolve<UserRelatedInfoService>();
return userRelatedInfoService.GetForUser(Id);
}
}
}
}
The anti pattern allows to write very small code working perfectly well, but violating some of the principles of DI (due to "service locator" + duplicated Container, having each their own lifetime).
This implementation also have the advantage to instantiate UserRelatedInfoService only when it's actually needed, if the related property of User is actually called (please keep in mind the real world example is much more complicated and some of the operations related to this may have a cost)
In the real world example, I have this situation in many assemblies, each of them needing to be able to resolve dependencies by the same way.
My question is: without to modify the User constructor, and the constructors of all objects instantiating some User, is there a clean way to avoid this?
By some kind of "dynamic resolving" of dependencies for example?
Please note that User is not in the same assembly as my Program class, so I can't access the original Container as a public property.
One solution I thought was to keep the class DependencyLocator but to remove its content, and just assign its Container property with the one created in main.
edit : FYI, so far I just followed my own suggestion and modified DependencyLocator to avoid it to rebuild its own container, and just set on it the final container built at entry point of application. It was an easy change to do and it avoids most of the problems pointed in original question.
At least, the code will always use the same container.
Thanks for reading!
For edge cases like this where you need runtime resolution by type, you can register IServiceProvider or a Func (Or a Func with the object[] being input parameters)
builder.Register(ctx => ctx as IServiceProvider ??
ctx.Resolve<ILifetimeScope>() as IServiceProvider)
.InstancePerLifetimeScope().AsSelf();
or
builder.Register(c =>
{
var scope = c.Resolve<ILifetimeScope>();
return (Func<Type, object>)(t => scope.Resolve(t));
}).As<Func<Type, object>>();
I have this api client ICommunicationClient(url, tenant) registered in my IoC container. Now I'm facing the scenario where I can have 1 to n api clients. I need to register all of them and I'm not sure how to handle that. I've seen there's this RegisterCollection in SI though.
I'm considering use a ICommunicationClientProvider as a wrapper around the actual clients. It contains a list with all the registered clients and methods to retrieve them. I feel this is not the best approach and of course, it "forces" me to touch other pieces of the app.
public class CommunicationClientProvider : ICommunicationClientProvider
{
public CommunicationClientCollection CommunicationClientsCollection { get; set; }
public string Tenant { get; set; }
public ICommunicationClient GetClients()
{
return CommunicationClientsCollection[Tenant];
}
public void SetClients(CommunicationClientCollection clients)
{
CommunicationClientsCollection = clients;
}
}
public interface ICommunicationClientProvider
{
ICommunicationClient GetClients();
void SetClients(CommunicationClientCollection clients);
}
This to host the collection
public class CommunicationClientCollection : Dictionary<string, ICommunicationClient>
{
}
Here I register the collection against SI
var clients = new CommunicationClientProvider();
foreach (var supportedTenant in supportedTenants)
{
clients.CommunicationClientsCollection
.Add(supportedTenant, new CommunicationClient(
new Uri(configuration.AppSettings["communication_api." + supportedTenant]),
new TenantClientConfiguration(supportedTenant)));
}
container.RegisterSingleton<ICommunicationClientProvider>(clients);
Do you know a better way of doing this? This is a normal scenario for example when you have multiple databases.
UPDATE: - ITenantContext part -
This is basically how my tenant context interface looks like:
public interface ITenantContext
{
string Tenant { get; set; }
}
and this is where I'm making my call to communication api:
public class MoveRequestedHandler : IHandlerAsync<MoveRequested>
{
private readonly IJctConfigurationService _communicationClient;
private readonly ITenantContext _tenantContext;
public MoveRequestedHandler(IJctConfigurationService communicationClient, ITenantContext tenantContext)
{
_communicationClient = communicationClient;
_tenantContext = tenantContext;
}
public async Task<bool> Handle(MoveRequested message)
{
_tenantContext.Tenant = message.Tenant;
_communicationClient.ChangeApn(message.Imei, true);
return await Task.FromResult(true);
}
}
here I register the ITenantContext
container.RegisterSingleton<ITenantContext, TenantContext>();
The tenant is defined within the MoveRequested object (message.Tenant).
How can I make CommunicationClient aware of that tenant?
If adding an ICommunicationClientProvider abstraction causes you to make sweeping changes throughout your application, there is clearly something wrong. You should typically be able to add features and make changes without having to do sweeping changes. And as a matter of fact, I think your current design already allows this.
Your ICommunicationClientProvider) acts like a factory, and factories are hardly ever the right solution. Instead, your are much better of using the Composite design pattern. For instance:
sealed class TenantCommunicationClientComposite : ICommunicationClient
{
private readonly ITenantContext tenantContext;
private readonly Dictionary<string, ICommunicationClient> clients;
public TenantCommunicationClientComposite(ITenantContext tenantContext,
Dictionary<string, ICommunicationClient> clients) {
this.tenantContext = tenantContext;
this.clients = clients;
}
object ICommunicationClient.ClientMethod(object parameter) =>
this.clients[this.tenantContext.CurrentTenantName].ClientMethod(parameter);
}
You can register this class as follows:
var dictionary = new Dictionary<string, ICommunicationClient>();
foreach (var supportedTenant in supportedTenants) {
dictionary.Add(supportedTenant, new CommunicationClient(
new Uri(configuration.AppSettings["communication_api." + supportedTenant]),
new TenantClientConfiguration(supportedTenant)));
}
container.RegisterSingleton<ICommunicationClient>(
new TenantCommunicationClientComposite(
new AspNetTenantContext(),
dictionary));
Here the ITenantContext is an abstraction that allows you to get the current tenant on who's behalf the current request is running. The AspNetTenantContext is an implementation that allows you to retrieve the current tenant in an ASP.NET application. You probably already have some code to detect the current tenant; you might need to move that code to such AspNetTenantContext class.