在一个ASP.NET MVC 5应用程序中,我有以下StructureMap配置:
cfg.For(typeof(IRequestHandler<,>)).DecorateAllWith(typeof(MediatorPipeline<,>));
有人知道如何在ASP.NET Core IOC中进行此配置吗?
在一个ASP.NET MVC 5应用程序中,我有以下StructureMap配置:
cfg.For(typeof(IRequestHandler<,>)).DecorateAllWith(typeof(MediatorPipeline<,>));
有人知道如何在ASP.NET Core IOC中进行此配置吗?
使用Scrutor。只需安装nuget包,然后执行以下操作。
services.AddSingleton<IGreeter, Greeter>();
services.Decorate<IGreeter, GreeterLogger>();
services.Decorate<IGreeter, GreeterExceptionHandler>();
顺序很重要。在上面的例子中, GreeterLogger 装饰了 Greeter。而 GreeterExceptionHandler 又装饰了 GreeterLogger。
当然,您也可以使用流行的 Autofac 框架。
如果想了解如何配置 Autofac,请查看 Ardalis Clean Arch 模板。
开箱即用的IoC容器没有支持装饰器模式或自动发现功能,据我所知这是“有意为之”的设计。
其想法是提供一个基本的IoC结构,可直接使用,或可以将其他IoC容器插入以扩展默认功能。
因此,如果您需要任何高级功能(例如支持特定构造函数、自动注册实现接口的所有类型或注入装饰器和拦截器),您必须编写自己的代码或使用提供此功能的IoC容器。
services.AddDecorator<IEmailMessageSender, EmailMessageSenderWithRetryDecorator>(decorateeServices =>
{
decorateeServices.AddScoped<IEmailMessageSender, SmtpEmailMessageSender>();
});
这个解决方法并没有将装饰器应用到某个类型的所有实例中,而是使用扩展方法将装饰器逻辑抽象成另一个文件。
定义装饰器结构如下:
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;
}
}
并像这样调用:
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.RegisterQueryHandler<FindThingByIdQueryHandler, FindThingByIdQuery, Thing>();
目前还有Scrutor包正在开发中。
这些回答似乎都没有满足问题的要求,即“如何在不指定类型的情况下对泛型进行注释?”由于该问题比较久远,当时可能还无法实现。当您查看Scrutor(来自2017年)时,答案是“由于基础DI框架,您无法这样做”-https://github.com/khellang/Scrutor/issues/39
我对此感到非常困惑,因为我已经可以在Microsoft DI框架中直接使用它了。有人能看到任何问题吗?
感谢那些首先使装饰器起作用的人们。
public static IServiceCollection AddDecorator(this IServiceCollection services, Type matchInterface, Type decorator, params Assembly[] assemblies)
{
Constraint.Requires(matchInterface.IsInterface, "Must be an interface to match");
Constraint.Requires(!decorator.IsInterface, "Must be a concrete type");
Constraint.Requires(assemblies.Length > 0, "Must provide at least one assembly for scanning for decorators");
var decoratedType = assemblies.SelectMany(t => t.GetTypes())
.Distinct()
.SingleOrDefault(t => t == decorator.GetGenericTypeDefinition());
if (decoratedType == null)
{
throw new InvalidOperationException($"Attempted to decorate services of type {matchInterface.Name} with decorator {decorator.Name} but no such decorator found in any scanned assemblies.");
}
foreach (var type in services
.Where(sd =>
{
try
{
return sd.ServiceType.GetGenericTypeDefinition() == matchInterface.GetGenericTypeDefinition();
}
catch (InvalidOperationException)
{
return false;
}
}).ToList())
{
var decoratedInstanceType = decoratedType.MakeGenericType(type.ServiceType.UnderlyingSystemType.GenericTypeArguments);
//Create the object factory for our decorator type, specifying that we will supply the interface injection explicitly
var objectFactory = ActivatorUtilities.CreateFactory(decoratedInstanceType, new[] {type.ServiceType});
//Replace the existing registration with one that passes an instance of the existing registration to the object factory for the decorator
services.Replace(ServiceDescriptor.Describe(
type.ServiceType,
s => objectFactory(s, new[] {s.CreateInstance(type)}),
type.Lifetime));
}
return services;
}
使用方法:
services
.AddDecorator(typeof(IAsyncCommandHandler<>), typeof(LoggingCommandDecorator<>), typeof(LoggingCommandDecorator<>).Assembly)
.AddDecorator(typeof(IAsyncCommandHandler<>), typeof(TracingCommandDecorator<>), typeof(TracingCommandDecorator<>).Assembly)
.AddDecorator(typeof(IAsyncQueryHandler<,>), typeof(TracingQueryDecorator<,>), typeof(TracingQueryDecorator<,>).Assembly);
Constraint.Requires
的快速问题,它是自己开发的功能还是一个包? - Michel另一个例子之一
services.AddTransient<Greeter>();
services.AddTransient<IGreeter>(g=>
ActivatorUtilities.CreateInstance<GreeterLogger>(g,g.GetRequiredServices<Greeter>())
);
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>()));
}
如果由于某种原因您无法使用Scrutor,这可能会有所帮助:
public static class ServiceCollectionExtensions
{
public static void AddWithDecorators<TService, TImplementation>(
this ServiceCollection serviceCollection, IEnumerable<Type> decorators, ServiceLifetime serviceLifetime)
{
serviceCollection.Add(new ServiceDescriptor(typeof(TImplementation), typeof(TImplementation), serviceLifetime));
var inner = typeof(TImplementation);
foreach (var decoratorType in decorators)
{
var innerCopy = inner;
var sd = new ServiceDescriptor(decoratorType,
sp => ActivatorUtilities.CreateInstance(sp, decoratorType, sp.GetRequiredService(innerCopy)),
serviceLifetime);
serviceCollection.Add(sd);
inner = decoratorType;
}
serviceCollection.Add(new ServiceDescriptor(typeof(TService), sp => sp.GetRequiredService(inner), serviceLifetime));
}
public static void AddWithDecorator<TService, TImplementation, TDecorator>(this ServiceCollection serviceCollection,
ServiceLifetime serviceLifetime)
=> AddWithDecorators<TService, TImplementation>(
serviceCollection,
new[] { typeof(TDecorator) },
serviceLifetime);
public static void AddWithDecorators<TService, TImplementation, TDecorator1, TDecorator2>(
this ServiceCollection serviceCollection, ServiceLifetime serviceLifetime)
=> AddWithDecorators<TService, TImplementation>(
serviceCollection,
new[] { typeof(TDecorator1), typeof(TDecorator2) },
serviceLifetime);
public static void AddWithDecorators<TService, TImplementation, TDecorator1, TDecorator2, TDecorator3>(
this ServiceCollection serviceCollection, ServiceLifetime serviceLifetime)
=> AddWithDecorators<TService, TImplementation>(
serviceCollection,
new[] { typeof(TDecorator1), typeof(TDecorator2), typeof(TDecorator3) },
serviceLifetime);
}
用法:
var sc = new ServiceCollection();
sc.AddWithDecorators<IStore<NamedEntity>, SimpleStore<NamedEntity>, CachedStore<NamedEntity>, WrapperStore<NamedEntity>>(ServiceLifetime.Singleton);
或者
sc.AddWithDecorators<IStore<NamedEntity>, SimpleStore<NamedEntity>>(new[] { typeof(CachedStore<NamedEntity>), typeof(WrapperStore<NamedEntity>) },
ServiceLifetime.Singleton);