I use a MediatR library in my Asp .Net core 6 app. And I have a the same logic for a group of requests, so I want to use the same handler for all of them. The problem is not new, but the solutions that I found did not help me. I have my own solution to solve this problem, but I do not like it and hope somebody knows a better way to solve this problem.
The example is shown here:
public class HandlerRequest<T> : IRequest
{
public T Data { get; set; }
}
public class Handler<T> : IRequestHandler<HandlerRequest<T>>
{
public Task<Unit> Handle(HandlerRequest<T> request, CancellationToken cancellationToken)
{
// the logic is here
return Task.FromResult(new Unit());
}
}
The registration of MediatR is as follows:
builder.Services.AddMediatR(typeof(BusinessLayer));
When I try to call my mediatr for example:
await _mediator.Send(new HandlerRequest<int>{Data = 5});
await _mediator.Send(new HandlerRequest<string>{Data = "TEST"});
I get an exception:
Error constructing handler for request of type
MediatR.IRequestHandler2[AT.Messenger.Credentials.Business.Requests.V1.Modeles.HandlerRequest1[System.Int32],MediatR.Unit].
Register your handlers with the container. See the samples in GitHub
for examples.
The problem happens because MediatR can not register generic handler. I also tried to register handler as scoped or singleton service as I've seen that advice on the internet, but it does not help.
The only solution that I found - is inherit a new class from my handler:
public class HandlerInt : Handler<int>
{
}
public class HandlerString : Handler<string>
{
}
If use this approach, all works, but I don't like this solution. If anybody know better way to solve this problem give me an advice please.
You have the following options, first, register what you will need explicitly like this.
services.AddTransient<IRequestHandler<HandlerRequest<int>, Unit>>, Handler<int>>();
//so on and so forth
This way you have registered the handlers for known types.
If you have open-bound generic, you can look into my PR that gives you a
services.RegisterGenericMediatRHandlers(typeof(GenericHandlerBase).Assembly);
to automatically register classes inheriting from the constrained interface by using the method provided above.
Lastly, you can opt to use Autofac and register all your handlers like this:
var mediatrOpenTypes = new[]
{
typeof(IRequestHandler<,>),
typeof(IRequestExceptionHandler<,,>),
typeof(IRequestExceptionAction<,>),
typeof(INotificationHandler<>),
};
foreach (var mediatrOpenType in mediatrOpenTypes)
{
builder
.RegisterAssemblyTypes(_assemblies.ToArray())
.AsClosedTypesOf(mediatrOpenType)
.AsImplementedInterfaces();
}
//also by simply
//builder.RegisterGeneric(typeof(Handler<>)).AsImplementedInterfaces();
This will also register all the generic handlers and you won't even have to register it explicitly inside the DI.
btw
The method you used to handle generic registration is the way described in the docs, to quote
If you have an open generic not listed above, you'll need to register it explicitly. For example, if you have an open generic request handler, register the open generic types explicitly:
services.AddTransient(typeof(IRequestHandler<,>), typeof(GenericHandlerBase<,>));
This won't work with generic constraints, so you're better off creating an abstract base class and concrete closed generic classes that fill in the right types.
Source: docs link
I have colleted a lot of information and figured out a solution to register any kind of generic Mediator Handlers.
I was stuck trying to register one generic implementation with another generic class inside INotificationHandler, like this:
public class DomainEventsDispatcher<TEvent> : INotificationHandler<DomainEventDispatch<TEvent>> where TEvent : DomainEvent
PS: I'll use the example above to explain below.
Obviously, your cannot simply use "services.AddMediatr(typeof(AppAssembly))" because using Mediator with generic types, Handlers are not loaded into DI on startup.
So, I decided to register each implementation of each type of TEvent, however, I did it by looping the assemblies that implemented TEvent type and building each part to be used on services.AddTransient.
Looking for assemblies that implemented TEvent type;
First variable: Build the generic used inside INotificationHandler;
Second variable: Build the INotificationHandler with first variable;
Third variable: Build the generic implementation with TEvent type;
Returing the list ready to be injected;
Here is an extension I did to loop and build necessary types to register on DI:
public static class AssemblyExtensions
{
public static List<(Type notificationHandler, Type implementation)> GetGenericNotificationHandlerForTypesOf(this Assembly assembly,
Type AbstractClassToFilter, Type GenericUsedWithNotificationHandler, Type ImplementationType, Type NotificationHandlerGeneric)
{
List<(Type, Type)> list = new List<(Type, Type)>();
foreach (Type item in from t in assembly.GetTypes()
where !t.IsAbstract && !t.IsInterface && t.BaseType == AbstractClassToFilter
select t)
{
var genericForNotificationHandler = GenericUsedWithNotificationHandler.MakeGenericType(item);
var notificationHandler = NotificationHandlerGeneric.MakeGenericType(genericForNotificationHandler);
var implementation = ImplementationType.MakeGenericType(item);
list.Add((notificationHandler, implementation));
}
return list;
}
}
Here is how to use (please take a look in the first example to understand the code below):
var registerPairs = typeof(DomainEvent).Assembly.GetGenericNotificationHandlerForTypesOf(typeof(DomainEvent), typeof(DomainEventDispatch<>), typeof(DomainEventsDispatcher<>), typeof(INotificationHandler<>));
registerPairs.ForEach(pair => services.AddTransient(pair.notificationHandler, pair.implementation));
Related
I am trying to optimize my code for the injection of a list of classes, that implement an interface of
IEventHandler<TEvent>.
I have the following structure:
public interface IEventHandlerMarker { }
public interface IEventHandler<in TEvent> : IEventHandlerMarker where TEvent : IEvent
{
Task Handle(TEvent eventItem);
}
public interface IEvent
{
public DateTime Timestamp { get; set; }
}
I register the marker interface IEventHandlerMarker in DI and when accessing the handlers, I currently do the following:
public EventPublisherService(IEnumerable<IEventHandlerMarker> eventHandlers)
{
// Check and and all event handlers
foreach (IEventHandlerMarker item in eventHandlers)
{
AddEventHandler(item);
}
}
In my AddEventHandler method, I filter those to IEventHandler<> like this:
Type handlerType = eventHandlerMarker.GetType().GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEventHandler<>));
So far everything works, but I'd like to get rid of the marker interface and the filter logic. So I changed the registration of handlers to the following method and this seems to work as expected:
public static IServiceCollection AddEventHandlers(this IServiceCollection serviceDescriptors, params Assembly[] handlerAssemblies)
{
Type genericHandlerType = typeof(IEventHandler<>);
foreach (var implementationType in genericHandlerType.GetTypesWithGenericInterfacesInAssemblies(handlerAssemblies))
{
Type interfaceType = implementationType.GetGenericInterfaceType(genericHandlerType);
serviceDescriptors.AddSingleton(interfaceType, implementationType);
}
return serviceDescriptors;
}
public static List<Type> GetTypesWithGenericInterfacesInAssemblies(this Type source, params Assembly[] assemblies)
{
return assemblies
.SelectMany(currentAssembly => currentAssembly.GetTypes()
.Where(type => type.GetInterfaces().Any(
interfaceItem => interfaceItem.IsGenericType
&& interfaceItem.GetGenericTypeDefinition().Equals(source))))
.ToList();
}
I changed the constructor of EventPublisherService to the following:
public EventPublisherService(IServiceProvider serviceProvider)
{
Type ienumerableOfIEventHandlerType = typeof(IEnumerable<>).MakeGenericType(typeof(IEventHandler<>));
object result = serviceProvider.GetService(ienumerableOfIEventHandlerType);
}
But result always turns out to be null.
I googled and checked some articles on Stackoverflow and came across the following article:
https://stackoverflow.com/a/51504151/1099519
I am not sure if this is the same case, as I am not using a factory.
Versions used: .NET Core 3.1 and Autofac 4.9.4 for the Dependency Injection management.
Register all handlers automatically as shown in this question/answer:
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.AsClosedTypesOf(typeof (IEventHandler<>)).AsImplementedInterfaces();
When you have TEvent and want to find all handlers, get them by constructing the concrete interface type as follows:
Type generic = typeof(IEnumerable<IEventHandler<>>);
Type[] typeArgs = { typeof(TEvent) }; // you might get the Type of TEvent in a different way
Type constructed = generic.MakeGenericType(typeArgs);
You should cache this in an dictionary to avoid doing reflection at every event dispatch.
Once you have the constructed concrete interface type, you can ask Autofac for all implementations of that interface:
var handlers = container.Resolve(constructed);
Now, the problem is that with the handler instances you can only call the Handle method using Invoke (reflection). This is a performance issue, but it's unrelated to how you register and resolve the handlers. It's related to the fact that you need to call a concrete method from an object. To avoid using reflection you need compiled code that calls the concrete method (note that using Generics also creates concrete compiled code for each Type).
I can think of two ways of getting compiled code to do this calls:
Manually writing a delegate which casts your object handler instance into a concrete type for every TEvent type that you have. Then store all these delegates in a dictionary so you can find them at runtime based on TEvent type and call it passing the handler instance and the event instance. With this approach, for every new TEvent that you create, you need to create a matching delegate.
Doing a similar thing as before but by emitting the code at startup. So, same thing, but the whole creation of the delegates is automatic.
Update
Based on the repository shared by the OP, I created a working version. The main code, to resolve the handlers and call them is in the EventPublisherService class
public class EventPublisherService : IEventPublisherService
{
private readonly ILifetimeScope _lifetimeScope;
public EventPublisherService(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
public async Task Emit(IEvent eventItem)
{
Type[] typeArgs = { eventItem.GetType() };
Type handlerType = typeof(IEventHandler<>).MakeGenericType(typeArgs);
Type handlerCollectionType = typeof(IEnumerable<>).MakeGenericType(handlerType);
var handlers = (IEnumerable<object>)_lifetimeScope.Resolve(handlerCollectionType);
var handleMethod = handlerType.GetMethod("Handle");
foreach (object handler in handlers)
{
await ((Task)handleMethod.Invoke(handler, new object[] { eventItem }));
}
}
}
Note that as specified in the original answer, this solution does not include the very necessary performance optimisations. The minimum that should be done is to cache all the types and MethodInfos so they don't need to be constructed every time. The second optimisation would be, as explained, to avoid using Invoke, but it's more complicated to achieve and IMO it requires a separate question.
With autofac you can inject an IEnumerable<IEventHandler<TEvent>> and Autofac should resolve a List of all implementations of it.
https://autofaccn.readthedocs.io/en/latest/resolve/relationships.html
I've tried to explain my admittedly complex problem to the best of my abilities. Let me know if there is anything I can add to clarify.
A brief background
I have a DbWrapperCollection I use to store DbWrapper<TInput. TOutput> (since TInput and TOutput will vary, the collection is really just a list of non-generic “containers” containing the generic as an object as well as the input and out as System.Types – see my implementation below)
On the other hand, I have a variable number of services, all with their own IDbWrapperCollection that I want to inject with autofac at startup.
Essentially what I want to do is this:
builder.RegisterType<SaveApplDbWrapper>().As<DbWrapper<AppleDto, SavedAppleDbEntity>>()
.Named<string>("fruitService");
builder.RegisterType<SaveOrangeDbWrapper>().As<IUcHandler<OrangeDto, OrangeDbEntity>>()
.Named<string>("fruitService");
builder.RegisterType<SaveMelon>().As<IUcHandler<MelonDto, MelonDbEntity>>()
.Named<string>("appleService");
builder.Register(c => new FruitService(c.ResolveNamed("appleService")))
.As<IDbMapperService>();
My problem
As you can see above, I specifically left out the expected type parameters when calling ResolveNamed(). That’s because I, being new to autofac (and to some extent, generics), I specifically don’t know if there are any strategies to inject a list of open generic DbWrappers and defer closing of my generic type.
I will try to explain which strategies i've researched for dealing with this below, as well as my implementation so far
My own research
The way I see it, I could either create a non-generic baseclass for my wrappers and save them as that baseclass, delegating the responsibility of resolving the original generic type to that baseclass, or ditch my wrapper collection idea in favor of specific parameters on my service constructor (boring – and not compatible with my composite-inspired implementation).
With the popularity of composite pattern, I’d imagine I’m not the first person with a composite-pattern-like solution with “generic leafs” wanting to use DI and an IoC.
I’m planning to use my fruitService like this:
myFruitService.GetDbMapper<MyFruitDto, DbEntityForThatSaidFruit(myOrange);
The service looks in it’s DbMapperCollection, finds the mapper with the provided type arguments, and call its implementation of Save();
Implementation so far
For those curious, here is my implementations:
DbWrappers:
class SaveApplebWrapper : DbWrapper<TInput, TOutput>
// and plenty more wrapppers for any fruit imaginable
Service:
public abstract class DbMapperService : IDbMapperService
{
public IWrapperCollection Wrappers { get; set; }
protected BestandService(IWrapperCollection wrappers)
{
Wrappers = wrappers;
}
public DbWrapper<TInput, TResult> GetWrapper<TInput, TResult>()
{
return Wrappers.GetWrapper<TInput, TResult>();
}
}
My WrapperCollection helper classes:
public struct WrapperKey
{
public static WrapperKey NewWrapperKey <TInput, TResult>()
{
return new WrapperKey { InputType = typeof(TInput), ResultType = typeof(TResult) };
}
public Type InputType { get; set; }
public Type ResultType { get; set; }
}
public struct WrapperContainer
{
public WrapperContainer(object wrapper) : this()
{
Wrapper= wrapper;
}
public object Wrapper{ get; set; }
public DbWrapper<TInput, TResult> GetWrapper<TInput, TResult>()
{
return Wrapper as DbWrapper<TInput, TResult>;
}
}
And my WrapperCollection:
public class UcWrapperCollection : Dictionary<WrapperKey, WrapperContainer>,
IDbWrapperCollection
{
public void AddWrapper<TInput, TResult>(UcHandler<TInput, TResult> handler)
{
Add(WrapperKey.NewWrapperKey<TInput, TResult>(), new WrapperContainer(handler));
}
public DbWrapper<TInput, TResult> GetWrapper<TInput, TResult>()
{
var key = WrapperKey.NewWrapperKey<TInput, TResult>();
return this[key].GetWrapper<TInput, TResult>();
}
}
Questions I've looked at without luck
Some questions I've looked at, none of which seemed relevant to my case (although my problem could potentially be solved with a generic delegate, I don't think it's a very optimal solution for my problem.
Injecting Generic type parameters with Autofac
Autofac. How to inject a open Generic Delegate in constructor
How to inject a factory of generic types with Autofac
Autofac with nested open generics
I don't think you're going to be able to do what you want. Sorry, probably not the answer you'd like. I'll show you why, and maybe some workarounds, but having an arbitrary collection of closed generics that don't get closed until resolution isn't really a thing.
Let's ignore DI for a second and just consider FruitService, which I don't see in the question, but which we see in a usage here:
builder.Register(c => new FruitService(c.ResolveNamed("appleService")))
.As<IDbMapperService>();
Note we can see that FruitService implements IDbMapperService because it's registered as that interface.
Further, we can see FruitService looks like it should take some sort of collection of things, since there are two things named the same in the registration example.
builder.RegisterType<SaveApplDbWrapper>().As<DbWrapper<AppleDto, SavedAppleDbEntity>>()
.Named<string>("fruitService");
builder.RegisterType<SaveOrangeDbWrapper>().As<IUcHandler<OrangeDto, OrangeDbEntity>>()
.Named<string>("fruitService");
I noticed that these both implement different generic types. I have to assume based on the rest of the question that these have no common base class.
To make it more concrete and get past the Autofac part, which I don't think is really relevant to the larger issue, let's consider it like this:
var wrapper = new[] { CreateWrapper("appleService"), CreateHandler("appleService") };
var service = new FruitService(wrapper);
Let's assume CreateWrapper and CreateHandler both take a string and, magically, creates the appropriate wrapper/handler types. Doesn't matter how it happens.
There are two things to consider here that relate closely:
What is the type of the parameter in the FruitService constructor?
What do you expect CreateWrapper("appleService") and CreateHandler("appleService") to return?
There are basically only two options here I can see.
Option 1: Use object.
If there's no common base class, then everything has to be object.
public class FruitService : IDBMapperService
{
private readonly IEnumerable<object> _wrappers;
public FruitService(IEnumerable<object>wrapper)
{
this._wrapper = wrapper;
}
public object GetWrapper<TInput, TResult>()
{
object foundWrapper = null;
// Search through the collection using a lot of reflection
// to find the right wrapper, then
return foundWrapper;
}
}
It's not clear that DbWrapper<TInput, TResult> can be cast to IUcHandler<TInput, TResult> so you can't even rely on that. There's no commonality.
But let's say there is common base class.
Option 2: Use the common base class
It seems there's already a notion of DbWrapper<TInput, TResult>. It's important to note that even if you have that generic defined, once you close it, they're two different types. DbWrapper<AppleDto, SavedAppleDbEntity> is not castable to DbWrapper<OrangeDto, SavedOrangeDbEntity>. Generics are more like "class templates" than base classes. They're not the same thing.
You can't, for example, do:
var collection = new DbWrapper<,>[]
{
new DbWrapper<AppleDto, SavedAppleDbEntity>(),
new DbWrapper<OrangeDto, SavedOrangeDbEntity>()
};
However, if you have a common interface or base class, you can do...
var collection = new IDbWrapper[]
{
new DbWrapper<AppleDto, SavedAppleDbEntity>(),
new DbWrapper<OrangeDto, SavedOrangeDbEntity>()
};
But that'd mean you can switch to that and, ostensibly, use the common interface.
public class FruitService : IDBMapperService
{
private readonly IEnumerable<object> _wrappers;
public FruitService(IEnumerable<object>wrapper)
{
this._wrapper = wrapper;
}
public IDbWrapper GetWrapper<TInput, TResult>()
{
IDbWrapper foundWrapper = null;
// Search through the collection using a lot of reflection
// to find the right wrapper, then
return foundWrapper;
// IDbWrapper could expose those `TInput` and `TResult`
// types as properties on the interface, so the reflection
// could be super simple and way more straight LINQ.
}
}
Your consuming code could just take IDbWrapper and call non-generic methods to get things done.
Bringing it back to Autofac...
Remember I mentioned the key was in figuring out what the Create methods should return; or what the FruitService constructor expects? That. That in spades.
You could register everything as keyed objects.
builder.RegisterType<SaveApplDbWrapper>()
.Named<object>("fruitService");
builder.RegisterType<SaveOrangeDbWrapper>()
.Named<object>("fruitService");
builder.RegisterType<SaveMelon>()
.Named<object>("appleService");
builder
.Register(c => new FruitService(c.ResolveNamed<IEnumerable<object>>("appleService")))
.As<IDbMapperService>();
The Resolve operations in Autofac are the create methods from my example. There's no magic there; it's just creating objects. You still have to know what type you want it to provide.
Or you could use a common base class.
builder.RegisterType<SaveApplDbWrapper>()
.Named<IDbWrapper>("fruitService");
builder.RegisterType<SaveOrangeDbWrapper>()
.Named<IDbWrapper>("fruitService");
builder.RegisterType<SaveMelon>()
.Named<IDbWrapper>("appleService");
builder
.Register(c => new FruitService(c.ResolveNamed<IEnumerable<IDbWrapper>>("appleService")))
.As<IDbMapperService>();
If you don't mind mixing the DI system into the FruitService you can do something like this:
public class FruitService
{
private readonly ILifetimeScope _scope;
public FruitService(ILifetimeScope scope)
{
this._scope = scope;
}
public DbWrapper<TInput, TResult> GetWrapper<TInput, TResult>()
{
var type = typeof(DbWrapper<TInput, TResult>);
var wrapper = this._lifetimeScope.Resolve(type);
return wrapper;
}
}
You'd have to register things without them being named and As a DbWrapper, but it'd work if everything is based on that.
builder.RegisterType<SaveApplDbWrapper>()
.As<DbWrapper<AppleDto, SavedAppleDbEntity>>();
// Must be DbWrapper, can also be other things...
builder.RegisterType<SaveOrangeDbWrapper>()
.As<IUcHandler<OrangeDto, OrangeDbEntity>>()
.As<DbWrapper<OrangeDto, OrangeDbEntity>>();
builder.RegisterType<SaveMelon>()
.As<DbWrapper<MelonDto, MelonDbEntity>>()
.As<IUcHandler<MelonDto, MelonDbEntity>>();
builder.RegisterType<FruitService>()
.As<IDbMapperService>();
When you resolve IDbMapperService the FruitService constructor will get a reference to the lifetime scope from which it was resolved. All of the wrappers will be resolved from that same scope.
Folks generally don't like mixing IoC references into their code like this, but it's the only way I can see you'd get away from having to mess with reflection or casting up and down all over.
Good luck!
Let's say I have several implementations of an interface:
public interface IDemoInterface
{
void DemoMethod();
}
public class DemoClass1 : IDemoInterface
{
public void DemoMethod()
{
Console.WriteLine("DemoClass1");
}
}
public class DemoClass2 : IDemoInterface
{
public void DemoMethod()
{
Console.WriteLine("DemoClass2");
}
}
Now, I am trying to define an array of such implementations and for each implementation call the interface method. Essentially, do something like this (what follows is a NON-WORKING code -- just an illustration of what I am trying to do):
public static void Run()
{
var demoClasses = { DemoClass1, DemoClass2};
foreach (var demoClass in demoClasses)
{
var implementation = demoClass as IDemoInterface;
implementation.DemoMethod();
}
}
What is the best way to accomplish it? Is using Reflection the only way?
From your example, it looks like you're not trying to loop through instances of classes that implement your interface. You're trying to loop through the types themselves. I'm basing it on this...
var demoClasses = { DemoClass1, DemoClass2};
...and in your question DemoClass and DemoClass2 are class types, not instances of types.
You could start with a collection of types and then create an instance of each type, like this. (You could also use reflection to find the class types that implement your interface.)
var types = new Type[] {typeof(DemoClass1), typeof(DemoClass1)};
foreach (var type in types)
{
if (typeof(IDemoInterface).IsAssignableFrom(type))
{
var instance = Activator.CreateInstance(type) as IDemoInterface;
instance.DemoMethod();
}
}
That will work, but there's a huge limitation: Each class must have a default constructor. Or if you're going to call a constructor, they would all need to have the same one.
In other words, if one of these classes has a constructor like this:
public DemoClass1(string connectionString){
then Activator.CreateInstance will blow up unless you give it the values to pass into the constructor. But if you have an array of types and they have different constructors then it's pretty much impossible to do that. And you don't want to paint yourself into a corner where you have to write classes that can only have empty constructors.
When you have an interface and classes that implement that interfaces, and you want to get instances of those classes, a dependency injection container (Ioc container) can be helpful. If you haven't used it there's a tiny bit of a learning curve, but it's a very useful tool to have in your box. Here's an example (this is from my blog) of configuring a Windsor container to "register", or declare several implementations of an interface.
(If you're not familiar with this it's going to seem totally out of nowhere, which is why I also linked to Windsor's documentation.)
public class DependencyRegistration : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
container.Register(Component.For<OrderValidator>());
container.Register(
Component.For<IOrderValidator,AddressValidator>()
.Named("AddressValidator"),
Component.For<IOrderValidator,OrderLinesValidator>()
.Named("OrderLinesValidator"),
Component.For<IOrderValidator,ProductStateRestrictionsValidator>()
.Named("ProductStateRestrictionsValidator")
);
}
}
In this example I've told this "container" that there are three classes that implement IOrderValidator.
Now this container can "resolve", or return, an array of implementations of IOrderValidator. Notice this line near the top:
container.Register(Component.For<OrderValidator>());
Here's that class:
public class OrderValidator : IOrderValidator
{
private readonly IOrderValidator[] _subValidators;
public OrderValidator(IOrderValidator[] subValidators)
{
_subValidators = subValidators;
}
public IEnumerable<ValidationMessage> GetValidationMessages(Order order)
{
var validationMessages = new List<ValidationMessage>();
foreach(var validator in _subValidators)
{
validationMessages.AddRange(validator.GetValidationMessages(order));
}
return validationMessages;
}
}
Notice that in the constructor there is an array of IOrderValidator. If I were to call
var validator = container.Resolve<OrderValidator>();
the container would create an instance of OrderValidator. It would detect that the constructor requires an array of IOrderValidator, so it would create instances of all of those other classes and place them in the array.
If any of those classes also had constructors that required other values or classes, and I told the container how to create those, it would create those as needed in order to be able to create the implementations of IOrderValidator.
The result is that I can have numerous implementations of a class, each with different constructor dependencies of their own, and I can create a collection of those implementations.
This answer doesn't go far toward really telling you how to do this, but hopefully it shows where to find the tools to accomplish this sort of thing. DI containers are very useful tools when used correctly. It's common practice in large applications and even small ones because it makes it easier to manage lots and lots of class types that may be nested inside one another while keeping it sane and understandable. It also helps us to write classes that are smaller and easier to unit test.
In addition to Windsor some other containers are Autofac, Unity, SimpleInjector, and the DI container that's included with ASP.NET Core applications.
Your code is mostly correct:
private static void Main()
{
var demoClasses = new List<IDemoInterface> {new DemoClass1(), new DemoClass2()};
foreach (var demoClass in demoClasses)
{
demoClass.DemoMethod();
}
Console.Write("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output
From the question, it is not clear for me if you wish to initialize the types yourself, or if you want to do it like Rufus's answer.
However, if you do not know the implementation beforehand, you could theoretically scan for them, and retrieve all the types
// can throw a ReflectionTypeLoadException in case not all dlls are known
private static IEnumerable<Type> GetImplementationsOf<TInterface>() {
var interfaceType = typeof( TInterface );
return AppDomain.CurrentDomain.GetAssemblies()
.Select( assembly => assembly.GetTypes().Where( type => !type.IsInterface && interfaceType.IsAssignableFrom( type ) ) )
.SelectMany( implementation => implementation );
}
This code will return a IEnumerable<Type> that matches the generic type parameter, and which isn't an interface (though theoretically, this part you could leave for your consumer to handle)
Afterwards, you can run through the list of types and create an instance of them and run your code, like so:
var types = GetImplementationsOf<IDemoInterface>();
foreach (var type in types) {
// will throw an exception in case there is no parameterless constructor
var impl = (IDemoInterface) Activator.CreateInstance( type );
impl.DemoMethod();
}
I have something like the following class:
public class MessagePublisher : IMessagePublisher
{
private readonly IComponentContext componentContext;
public MessagePublisher(IComponentContext componentContext)
{
this.componentContext = componentContext;
}
public void PublishMessage<T>(T message) where T : class
{
var messageConsumers = componentContext.Resolve<IEnumerable<IMessageConsumer<T>>>();
foreach (IMessageConsumer<T> messageConsumer in messageConsumers)
{
// Do some stuff.
}
}
}
}
This... doesn't work. I get an error from Autofac indicating I can't store the component context like I have, which makes sense.
This resolve operation has already ended. When registering components using lambdas, the IComponentContext 'c' parameter to the lambda cannot be stored. Instead, either resolve IComponentContext again from 'c', or resolve a Func<> based factory to create subsequent components from.
I totally get why this doesn't work. What I'm wondering is if there's an alternate way to do what I'm trying to do, which is to resolve a type within a method (I can't pass it in via the constructor, since I don't know the type at that point). Any ideas? Thanks for your help.
You can do it, but you must have the registration wrong. I'm guessing you have something like...
builder.Register(c => new MessagePublisher(c));
but you need to do
builder.Register(c => new MessagePublisher(c.Resolve<IComponentContext>()));
That being said, giving normal classes direct access to your DI bits is usually an anti-pattern, but sometimes some plumbing/framework code may need to do so.
Is it difficult in simple injector to auto register these classes:
public class RepositoryA : Repository<Hammers>, IRepositoryA
{
... implementing code here ...
}
public interface IRepositoryA: IRepository<Hammers>
{ //no methods
}
public class RepositoryB : Repository<Knives>, IRepositoryB
{
... implementing code here ...
}
public interface IRepositoryB: IRepository<Knives>
{ //no methods
}
public class RepositoryC : Repository<Swords>, IRepositoryC
{
... implementing code here ...
}
public interface IRepositoryC: IRepository<Swords>
{ //no methods
}
I was following the documentation described here: Simple Injector - Batch Registration
But it seems that it doesn't work if you try to inherit from another class and its interface. I can't separate the interfaces, meaning, it gets Repository and IRespositoryX, where X is the letter for the repository (A, B, C, etc). It essentially throws an error:
Sequence contains more than one element
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Sequence contains
more than one element
It works if I manually register each class, one by one:
container.Register<IRepositoryA, RepositoryA>();
container.Register<IRepositoryB, RepositoryB>();
container.Register<IRepositoryC, RepositoryC>();
/* add more here */
Here is what I have so far:
var repositoryAssembly = typeof(StateRepository).Assembly;
var registrations =
from type in repositoryAssembly.GetExportedTypes()
where type.Namespace == "MappingTool.Repository"
where type.GetInterfaces().Any()
where !type.IsAbstract
select new
{
Service = type.GetInterfaces().Single(),
Implementation = type
};
foreach (var reg in registrations)
{
container.Register(reg.Service, reg.Implementation, Lifestyle.Transient);
}
The use case that Simple Injector supports is to batch register a set of implementations by their related generic interfaces. In other words, by doing this:
container.RegisterManyForOpenGeneric(
typeof(IRepository<>),
typeof(StateRepository).Assembly);
You are basically registering a closed version of the IRepository<T> interface as service, with its corresponding implementation. In other words, this will be equivalent to doing the following:
container.Register<IRepository<A>, RepositoryA>();
container.Register<IRepository<B>, RepositoryB>();
container.Register<IRepository<C>, RepositoryC>();
/* add more here */
Since you are not implementing the common use case, you will have to fall back to doing this manually. You can do this by writing the LINQ query as you already did, or you can make use of the OpenGenericBatchRegistrationExtensions.GetTypesToRegister method to make this easier:
var repositoryTypes = OpenGenericBatchRegistrationExtensions.GetTypesToRegister(
container, typeof(IRepository<>), repositoryAssembly);
foreach (Type implementationType in repositoryTypes)
{
Type serviceType =
implementationType.GetInterfaces().Where(i => !i.IsGenericType).Single();
container.Register(serviceType, implementationType, Lifestyle.Transient);
}
Do make sure though that you understand that you are violating the SOLID principles by implementing custom interfaces on your repositories. It be beneficial to prevent such violation. Take a look at this article for more information about this.