简体   繁体   English

如何使用 ASP.NET 核心依赖注入来应用装饰器

[英]How to apply decorators with ASP.NET Core Dependency Injection

On an ASP.NET MVC 5 application I have the following StructureMap configuration:在 ASP.NET MVC 5 应用程序上,我有以下 StructureMap 配置:

cfg.For(typeof(IRequestHandler<,>)).DecorateAllWith(typeof(MediatorPipeline<,>));

Does anyone know how to do this configuration with ASP.NET Core IOC?有谁知道如何使用 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.开箱即用的 IoC 容器不支持装饰模式或自动发现,据我所知,这是“按设计”。

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.这个想法是提供一个开箱即用的基本 IoC 结构,或者可以插入其他 IoC 容器以扩展默认功能。

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.因此,如果您需要任何高级功能(支持特定构造函数、自动注册实现接口或注入装饰器和拦截器的所有类型),您必须自己编写或使用提供此功能的 IoC 容器。

Use Scrutor .使用检查员 Just install the nuget package and then do the following.只需安装nuget包,然后执行以下操作。

services.AddSingleton<IGreeter, Greeter>();
services.Decorate<IGreeter, GreeterLogger>();
services.Decorate<IGreeter, GreeterExceptionHandler>();

The order is important.顺序很重要。 In the above, GreeterLogger decorates Greeter.在上面,GreeterLogger 装饰了 Greeter。 And GreeterExceptionHandler decorates GreeterLogger.而 GreeterExceptionHandler 装饰了 GreeterLogger。

If you need more info, take a look at this and this .如果您需要更多信息,请查看thisthis

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.还有正在开发的Scrutor包。

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 use或使用

static class ServiceProviderExtensions
{
    public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class => 
        ActivatorUtilities.CreateInstance<T>(provider, parameters);
}

from .NET Core DI, ways of passing parameters to constructor来自.NET Core DI,将参数传递给构造函数的方法

If for whatever reason you can't use Scrutor, this might help:如果由于某种原因您不能使用 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);
}

usage:用法:

var sc = new ServiceCollection();

sc.AddWithDecorators<IStore<NamedEntity>, SimpleStore<NamedEntity>, CachedStore<NamedEntity>, WrapperStore<NamedEntity>>(ServiceLifetime.Singleton);

or或者

sc.AddWithDecorators<IStore<NamedEntity>, SimpleStore<NamedEntity>>(new[] { typeof(CachedStore<NamedEntity>), typeof(WrapperStore<NamedEntity>) },
    ServiceLifetime.Singleton);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM