On an ASP.NET MVC 5 application I have the following StructureMap configuration:
cfg.For(typeof(IRequestHandler<,>)).DecorateAllWith(typeof(MediatorPipeline<,>));
Does anyone know how to do this configuration with ASP.NET Core IOC?
On an ASP.NET MVC 5 application I have the following StructureMap configuration:
cfg.For(typeof(IRequestHandler<,>)).DecorateAllWith(typeof(MediatorPipeline<,>));
Does anyone know how to do this configuration with ASP.NET Core IOC?
The out of the box IoC container doesn't support decorate pattern or auto discovery, which is "by design" as far as I know.
The idea is to provide a basic IoC structure that works out of the box or where other IoC containers can be plugged in to extend the default functionality.
So if you need any advanced features (support for a specific constructor, auto-registering of all types which implement an interface or inject decorators and interceptors) you have to either write it yourself or use an IoC container which offers this functionality.
Use Scrutor. Just install the nuget package and then do the following.
services.AddSingleton<IGreeter, Greeter>();
services.Decorate<IGreeter, GreeterLogger>();
services.Decorate<IGreeter, GreeterExceptionHandler>();
The order is important. In the above, GreeterLogger decorates Greeter. And GreeterExceptionHandler decorates GreeterLogger.
If you need more info, take a look at this and this.
And of course, you can use the popular Autofac as well.
If you want to know how to configure Autofac, take a look at Ardalis Clean Arch template
This workaround doesn't apply the decorator to all instances of a type but uses extension methods to abstract the decorator logic into another file.
Defining the decorator structure like:
public static class QueryHandlerRegistration
{
public static IServiceCollection RegisterQueryHandler<TQueryHandler, TQuery, TResult>(
this IServiceCollection services)
where TQuery : IQuery<TResult>
where TQueryHandler : class, IQueryHandler<TQuery, TResult>
{
services.AddTransient<TQueryHandler>();
services.AddTransient<IQueryHandler<TQuery, TResult>>(x =>
new LoggingDecorator<TQuery, TResult>(x.GetService<ILogger<TQuery>>(), x.GetService<TQueryHandler>()));
return services;
}
}
And calling it like:
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.RegisterQueryHandler<FindThingByIdQueryHandler, FindThingByIdQuery, Thing>();
There's also the Scrutor package being worked on.
In my blogpost I described how a relatively simple extension method can solve this problem easily. Here is an example from that post which shows how decorator configuration may look like:
services.AddDecorator<IEmailMessageSender, EmailMessageSenderWithRetryDecorator>(decorateeServices =>
{
decorateeServices.AddScoped<IEmailMessageSender, SmtpEmailMessageSender>();
});
one of another example
services.AddTransient<Greeter>();
services.AddTransient<IGreeter>(g=>
ActivatorUtilities.CreateInstance<GreeterLogger>(g,g.GetRequiredServices<Greeter>())
);
or generic
private static void AddTransientDecorated<TInterface,TService,TDecorator>(this IServiceCollection services)
{
services.AddTransient(typeof(TService));
services.AddTransient(typeof(TInterface), p => ActivatorUtilities.CreateInstance<TDecorator>(p, p.GetRequiredService<TService>()));
}
additional information .NET Core DI, ways of passing parameters to constructor