How to resolve IoC dynamically by using it's name as string - c#

I have a multiple Implementations for the same interface like the below example
container.Register(Component.For<OrganizationServiceCore.IOrganizationService>().ImplementedBy<SchoolServiceCore.SchoolsProvider.SchoolService>());
OR
container.Register(Component.For<OrganizationServiceCore.IOrganizationService>().ImplementedBy<CompanyServiceCore.CompanyProvider.CompanyService>());
The resolving must depend on a vale in the database,I will get the value from database as a string "SchoolServiceCore.SchoolsProvider.SchoolService" OR
"CompanyServiceCore.CompanyProvider.CompanyService"
How I can use it like the example below:
string serviceName= "CompanyServiceCore.CompanyProvider.CompanyService";
container.Register(Component.For<OrganizationServiceCore.IOrganizationService>().ImplementedBy<serviceName>());

You can try using
string serviceName= "CompanyServiceCore.CompanyProvider.CompanyService";
Type myType= Type.GetType(serviceName);
container.Register(Component.For<OrganizationServiceCore.IOrganizationService>().ImplementedBy<myType>());
and using this to register your type in the IoC container.

I would assume that you are using Windsor Castle as your DI container based on the provided sample code.
I can see at least two different options:
1) Register All and use only the appropriate one
container.Register(
Component.For<OrganizationServiceCore.IOrganizationService>()
.ImplementedBy<SchoolServiceCore.SchoolsProvider.SchoolService>(),
Component.For<OrganizationServiceCore.IOrganizationService>()
.ImplementedBy<CompanyServiceCore.CompanyProvider.CompanyService>());
In this case the first one would win. But if you call the Named builder function as well during the service registrations then you can resolve the appropriate one by its name.
container.Register(
Component.For<OrganizationServiceCore.IOrganizationService>()
.Named("SchoolService")
.ImplementedBy<SchoolServiceCore.SchoolsProvider.SchoolService>(),
Component.For<OrganizationServiceCore.IOrganizationService>()
.Named("CompanyService")
.ImplementedBy<CompanyServiceCore.CompanyProvider.CompanyService>());
...
IOrganizationService svc = container.Resolve<IOrganizationService>("SchoolService");
2) Register only the ONE that is needed
In this case you should use UsingFactoryMethod builder function to delegate the creation process of the appropriate service implementation.
container.Register(
Component.For<OrganizationServiceCore.IOrganizationService>()
.UsingFactoryMethod(
() => OrganizationServiceFactory.CreateService(serviceNameSetting)));
Comparison
The first approach allows to use multiple implementation at the same
time
The first approach chooses the appropriate implementation on
the usage side (this is the so called Service Locator pattern, which
should be avoided if possible if you use DI already)
The second approach registers only a single implementation
The second approach separates registration logic from type deduction logic
For further information please visit the Windsor documentation

var assemblyName = "CompanyServiceCore.CompanyProvider";
var serviceName = "CompanyService";
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyName);
var service = assembly.GetTypes().Where(p =>
p.FullName == assemblyName+"."+serviceName
).FirstOrDefault();
container.Register(Component.For<OrganizationServiceCore.IOrganizationService>().ImplementedBy(service));
you can change the assemblyName and serviceName variables depending on the needed value.
don't forgot to add the project reference on the place that you want to use it.

Related

Resolving dynamically registered services by key in .NET Core

I'm migrating a tool to a .net 5 console app. I would like to change my DI system, currently a modified version of TinyIoC, to use if possible the built-in DI. Currently, my tool will load and auto-register any dll it finds in its config file. First-in wins, so a user-provided implementation of one of my interfaces will take precedence over my default one, loaded last.
Additionally, I need to be able to register several variants of a given interface, and have my DI system choose between them based on configuration. Currently, this works with a RegistrationName attribute that I've added to Tiny. When tiny auto-registers everything in a dll, it includes this name in it's registration.
So, for example, I have a IProvider interface, with methods including IDbConnection GetConnection(string connectionString);. I have several default implementations for SQL Server, Postgres, etc. and users can provide other implementations in dlls that I don't know about when compiling my tool.
Here is how I declare my SQL Server provider...
[RegistrationName("System.Data.SqlClient")]
class SqlClient : IProvider
{
Here is how I specify provider in my qfconfig.json...
{
"defaultConnection": "Data Source=localhost;Initial Catalog=Northwind;Integrated Security=True",
"provider": "System.Data.SqlClient"
}
Here is how I ask Tiny for a concrete instance...
// RegistrationName is passed to the Resolve method.
// Tiny returns the implementation whose RegistrationName matches.
_provider = _tiny.Resolve<IProvider>(config.provider);
So I want to keep this possibility, but find a less personal way of doing it.
I fear I've strayed out into the forest on this subject. The docs and tutos that I find all cover much simpler scenarios, where there's one registered implementation of an interface, and everything is explicitly registered in code. Can someone point me back on the road please?
If I understand your use case correctly, you have possible multiple IProvider implementations, but always only need one at runtime, which is based on the the configured value that maps to the RegistrationName attribute.
There's nothing built-in to the MS.DI framework that simplifies such use case, but as you only need to register one at runtime, you can achieve this by iterating the assemblies and finding that specific implementation and register that:
var providers =
from assembly in assemblies
from type in assembly.GetExportedTypes()
where typeof(IProvider).IsAssignableFrom(type)
where !type.IsAbstract
let attr = type.GetCustomAttribute<RegistrationNameAttribute>()
where attr?.Name == config.provider
select type;
services.AddTransient(typeof(IProvider), providers.Single());
This way registration is based on the name, while resolving can be done in a keyless fashion:
serviceProvider.GetRequiredService<IProvider>();
In the case I misunderstood your question, and you need multiple IProvider implementations at runtime, and need to resolve them by their key... well, that's certainly possible, but you will have to write more code. And here's the takeaway from everything that follows, ActivatorUtilities is your friend:
// Find all 'name -> provider' mappings
var providerDefinitions =
from assembly in assemblies
from type in assembly.GetExportedTypes()
where typeof(IProvider).IsAssignableFrom(type)
where !type.IsAbstract
let name = type.GetCustomAttribute<RegistrationNameAttribute>()?.Name
where name != null
select new { name, type };
// Helper method that builds IProvider factory delegates
Func<IServiceProvider, IProvider> BuildProviderFactory(Type type) =>
provider => (IProvider)ActivatorUtilities.CreateInstance(provider, type);
// Create a dictionary that maps the name to a provider factory
Dictionary<string, Func<IServiceProvider, IProvider>> providerFactories =
providerDefinitions.ToDictionary(
keySelector: i => i.name,
elementSelector: i => BuildProviderFactory(i.type));
// Possible use
Func<IServiceProvider, IProvider> factory = providerFactories[config.provider];
IProvider provider = factory(serviceProvider);
ActivatorUtilities.CreateInstance is MS.DI's extension point that allows the creation of unregistered classes, while injecting them with dependencies that are part of the provided IServiceProvider instance.
ActivatorUtilities.CreateInstance comes with a lot of unfortunate subtle downsides, such as the inability to check for cyclic dependencies, which could lead to nasty stack overflow exceptions instead. But this is the best we can achieve with MS.DI. Other DI Containers are more mature and feature rich in this respect.

Structure map - understanding Use and Add semantics

I have the following code with NServiceBus & StructureMap 2.6.2 wired up together:
var bus = Configure.WithWeb().StructureMapBuilder(container)
ObjectFactory.Container.Configure(r =>
r.For<IBus>().Singleton().Use(Configure.Instance.CreateBus().Start())
);
container.Configure(r => r.For<IBus>().Singleton().Add<MyBus>().Named("named"));
I want first registration to be a default one, second registration to be available as a named one. But when I run:
var bus1 = container.GetInstance<IBus>();
var bus2 = container.GetInstance<IBus>("named");
I get both instances of type MyBus. According to this question first instance must come from first registration (of type UnicastBus) but it is not.
Am I understanding Use and Add semantics wrong?
You are registering the first instance in the ObjectFactory container instance. The second instance is being registered in a local container instance named container.
For this behavior to work right, you need to use the same container instance for both registrations.
On a side note, you should never use the ObjectFactory static instance (as per the documentation).
The static ObjectFactory wrapper for Container is still available in 3.0, but we strongly recommend against using it for new applications. It only exists for easier compatibility with older installations.
Both registrations:
ObjectFactory.Container.Configure(r =>
r.For<IBus>().Singleton().Use(Configure.Instance.CreateBus().Start())
);
and
container.Configure(r => r.For<IBus>().Singleton().Add<MyBus>().Named("named"));
are applied do different instances of container (container != ObjectFactory.Container). This results in container having only one registration of IBus (MyBus) and that is why you can only resolve this dependency. When you register only one type (even if it is named instance) for specific plugin type you are able to resolve it when using container.GetInstance<TPluginType>(). To fix this issue and have expected behavior you need to change this registration:
ObjectFactory.Container.Configure(r =>
r.For<IBus>().Singleton().Use(Configure.Instance.CreateBus().Start())
);
with this:
container.Configure(r =>
r.For<IBus>().Singleton().Use(Configure.Instance.CreateBus().Start())
);
Tested this issue in both versions of SM (2.6 and 3+) and the behavior is the same on these versions.
Hope this helps!

Using same parameter value in a dependency with Windsor

Not even sure this can be done and may need to re-think the whole thing, however thought I would ask before doing that.
Ok I have a repository class with the following constructor
public Repository(ISessionManager sessionManager, string dbName)
{
this.sessionManager = sessionManager;
this.dbName = dbName;
}
At present anywhere in the application that needs to use a repository it uses dependency injection and the repository are registered with the container like so.
Component.For<IRepository<User, int>>().ImplementedBy<Repository<User, int>>()
.DependsOn(Dependency.OnValue("dbName", "admin")),
What I am looking to do is push the setting of the database name up a level to the services which will use the repositories.
For example having a service with the constructor
public SecurityService(ITransactionFactory transactionFactory,
IRepository<User, int> userRepository,
string dbName)
I want the argument dbName to be registered with the container against ServiceService and have that value passed when injecting the argument userRepository.
So if another service needs to use the same repository with a different database then when it is registered a new name is provided.
Is this possible?
There's no way that I'm aware of to do exactly what you're looking for, but using named registrations may get you to the same end-goal in a way that's a bit more Windsor-"correct".
For instance, the following two registrations could live in tandem:
Component.For<IRepository<User, int>>()
.ImplementedBy<Repository<User, int>>()
.DependsOn(Dependency.OnValue("dbName", "admin")
.Named("adminRepositoryRegistration")
Component.For<IRepository<User, int>>()
.ImplementedBy<Repository<User, int>>()
.DependsOn(Dependency.OnValue("dbName", "user")
.Named("userRepositoryRegistration")
Then you could wire up your services to depend on the particular registrations in Windsor. For instance, this one would be bound to the first registration:
Component.For<IUserService>()
.ImplementedBy<UserService>()
.DependsOn(Dependency.OnComponent("userRepository", "userRepositoryRegistration")
and this will bind to the second:
Component.For<ISecurityService>()
.ImplementedBy<SecurityService>()
.DependsOn(Dependency.OnComponent("adminRepository", "adminRepositoryRegistration")
Overall I feel like it gives a bit cleaner of a solution, since it handles all of the dependency provisioning in the DI registration instead of leaving any loose ends to wrap up in the actual services, and allows you to route dependencies as needed.

Adding interception on existing registration

In this scenario I my application is handed an already initialized UnityContainer on which has been registered a type which boils down to this:
container.RegisterType<IService>(new InjectionFactory(c => new Service()));
What I need to achieve is adding an interceptor ServiceInterceptor to the IService registration. I suppose the obvious answer is: Do this by running a second RegisterType<IService> and applying the interceptor as injection members. However, re-creating the provided injection factory and delegate as described below is unfortunately not feasible. The new Service() statement isn't available to me at this point.
container.RegisterType<IService>(
new InjectionFactory(c => new Service()),
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<ServiceInterceptor>());
So: I am looking for a way to add further injection members to an existing ContainerRegistration.
// 1. Get the current container registration
var containerRegistration = container.Registrations
.First(cr => cr.RegisteredType == typeof(IService));
// 2. Is this even possible?
ApplyInterception(
containerRegistration,
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<ServiceInterceptor>());
// 3. Profit!
You could initially register the type as a named registration (using the InjectionFactory) while also providing a default registration (with no name) that just resolves the named registration:
container.RegisterType<IService>("original",
new InjectionFactory(c => new Service()));
container.RegisterType<IService>(
new InjectionFactory(c => c.Resolve<IService>("original")));
So you can resolve IService as you would normally do. However you will now be able to replace the default registration while keeping the original named registration. This way you can work around your issue, where you couldn't re-register IService due to the factory statement not being available at that point.
With this approach in place, at a later point you can override the default IService registration with one where interception is registered and still uses the original named registration for resolving the instance:
container.RegisterType<IService>(
new InjectionFactory(c => c.Resolve<IService>("original")),
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<ServiceInterceptor>());
If you now resolve IService, you will still use the original factory method c => new Service() as it is resolving the "original" named registration, but this time your ServiceInterceptor is also applied.
I have created this fiddle so you can check a full working example.
There is a second approach using Policy Injection. (See Policy Injection section in the msdn).
First configure your type as usual, but leave the door opened for using Policy Injection:
container.RegisterType<IService>(
new InjectionFactory(c => new Service()),
new InterceptionBehavior<PolicyInjectionBehavior>(),
new Interceptor<InterfaceInterceptor>());
At this point your service is registered without any interception being applied. However at a later point you can add a policy injection rule, for example matching your service type name, that adds the interception:
container.Configure<Interception>()
.AddPolicy("yourInterceptor")
.AddMatchingRule<TypeMatchingRule>
(new InjectionConstructor("MyNamespace.Service", true))
.AddCallHandler<ServiceInterceptorHandler>(
new ContainerControlledLifetimeManager(),
new InjectionConstructor(),
new InjectionProperty("Order", 1));
Now if you resolve IService, the interception logic in ServiceInterceptorHandler will be applied (This class is basically the same as ServiceInterceptor in the first approach, but implementing ICallHandler instead of IInterceptionBehavior)
Again, check the example in this fiddle
Having a look at both options, I personally feel more comfortable with the first approach, avoiding the overhead of the matching rules.
The first approach would also allow you to easily completely turn off interception by overriding again the IService registration, saving you from the interceptors overhead if you want it completely off. (Both approaches allow you to implement the WillExecute property of the interceptor/handler classes, but you still have the overhead of the interceptors). You can do this using policy injection, but you need another intermediate call handler, see this post
However with the second approach, you could apply this solution to multiple classes using the matching rules (For example all classes in a namespace, or all classes whose name follows a specific pattern, etc. You can take a look at the matching rules here)
In the end you will need to decide which one fits you best. (and there might be other approaches, would love to see them posted!)

Castle Windsor: Auto-register types from one assembly that implement interfaces from another

I use Castle Windsor as my IoC container. I have an application that has a structure similar to the following:
MyApp.Services.dll
IEmployeeService
IContractHoursService
...
MyApp.ServicesImpl.dll
EmployeeService : MyApp.Services.IEmployeeService
ContractHoursService : MyApp.Services.IContractHoursService
...
I use the XML configuration at the moment, and every time I add a new IService/Service pair, I have to add a new component to the XML configuration file. I want to switch all this over to the fluent registration API but haven't worked out exactly the right recipe to do what I want yet.
Can anyone help? The lifestyles will all be singleton.
Many thanks in advance.
With AllTypes you can easily do this:
From http://stw.castleproject.org/(S(nppam045y0sdncmbazr1ob55))/Windsor.Registering-components-by-conventions.ashx:
Registering components one-by-one can be very repetitive job. Also remembering to register each new type you add can quickly lead to frustration. Fortunately, you don't have to do it, at least always. By using AllTypes entry class you can perform group registration of types based on some specified characteristics you specify.
I think your registration would look like:
AllTypes.FromAssembly(typeof(EmployeeService).Assembly)
.BasedOn<IEmployeeService>()
.LifeStyle.Singleton
If you implement a base type, like IService on your interfaces, you can register them all at once using the following construct:
AllTypes.FromAssembly(typeof(EmployeeService).Assembly)
.BasedOn<IService>()
.WithService.FromInterface()
.LifeStyle.Singleton
For more examples, see the article. This has a very good description on what the possibilities are.
I took Pieter's answer forward just a little bit (the key being, as he suggested, AllTypes) and have come up with this:
// Windsor 2.x
container.Register(
AllTypes.FromAssemblyNamed("MyApp.ServicesImpl")
.Where(type => type.IsPublic)
.WithService.FirstInterface()
);
This goes through all public classes in the MyApp.ServicesImpl.dll assembly and registers each in the container using the first interface it implements. Because I want all the classes in the services assembly, I need no marker interface.
The above works for an old version of Windsor. The current Castle Windsor documentation for registering components for the latest version suggests the following:
// Windsor latest
container.Register(
AllTypes.FromAssemblyNamed("MyApp.ServicesImpl")
.Where(type => type.IsPublic) // Filtering on public isn't really necessary (see comments) but you could put additional filtering here
.WithService.DefaultInterface()
);

Categories

Resources