Autofac终身范围装饰器 [英] Autofac Lifetime Scope Decorator

查看:67
本文介绍了Autofac终身范围装饰器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Autofac实现命令处理程序模式,并使用其装饰器功能来处理诸如日志记录,身份验证等交叉问题.

I am implementing a command handler pattern using Autofac and am using it's decorator facility handle cross cutting concerns such as logging, authentication etc.

我也有一些依赖项,我只希望将它们的范围限制在请求/响应管道的生命周期之内.

I also have dependencies that I only want scoped to the lifetime of the request / response pipeline.

我在下面有一个示例实现:

I have an example implementation below:

public class Program
{
    public static void Main()
    {
        var builder = new ContainerBuilder();
        builder.RegisterAssemblyModules(typeof(HandlerModule).Assembly);
        builder.RegisterType<LifetimeScopeTester>().AsSelf()
            .InstancePerMatchingLifetimeScope("pipline");

        var container = builder.Build();

        using(var scope = container.BeginLifetimeScope("pipline")) {
            var pingHandler = scope.Resolve<IHandle<PingRequest, PingResponse>>();
            pingHandler.Handle(new PingRequest());
        }
    }
}

public class HandlerModule : Autofac.Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterAssemblyTypes(ThisAssembly)
            .As(type => type.GetInterfaces()
            .Where(interfaceType => interfaceType.IsClosedTypeOf(typeof (IHandle<,>)))
            .Select(interfaceType => new KeyedService("IHandle", interfaceType)));

        builder.RegisterGenericDecorator(
            typeof(SecondDecoratorHandler<,>),
            typeof(IHandle<,>),
            "IHandle"
        )
        .Keyed("SecondDecoratorHandler", typeof(IHandle<,>));

        builder.RegisterGenericDecorator(
            typeof(FirstDecoratorHandler<,>),
            typeof(IHandle<,>),
            "SecondDecoratorHandler"
        );
    }
}

public class LifetimeScopeTester {}

public interface IHandle<in TRequest, out TResponse> 
    where TRequest : class, IRequest<TResponse>
{
   TResponse Handle(TRequest request);
}

public interface IRequest<TResponse> {

}

public class PingRequest : IRequest<PingResponse> {

}

public class PingResponse {

}

public class PingHandler : IHandle<PingRequest, PingResponse> {
    public PingResponse Handle(PingRequest request) {
        Console.WriteLine("PingHandler");
        return new PingResponse();
    }
}

public class FirstDecoratorHandler<TRequest, TResponse> : IHandle<TRequest, TResponse>
    where TRequest : class, IRequest<TResponse>
{
    private readonly IHandle<TRequest, TResponse> _decoratedHandler;
    private readonly LifetimeScopeTester _lifetimeScopeTester;

    public FirstDecoratorHandler(IHandle<TRequest, TResponse> decoratedHandler,
        LifetimeScopeTester lifetimeScopeTester)
    {
        _decoratedHandler = decoratedHandler;
        _lifetimeScopeTester = lifetimeScopeTester;
    }

    public TResponse Handle(TRequest request)
    {
        Console.WriteLine("FirstDecoratorHandler - LifetimeScopeTester[{0}]",
            _lifetimeScopeTester.GetHashCode());
        return _decoratedHandler.Handle(request);
    }
}

public class SecondDecoratorHandler<TRequest, TResponse> : IHandle<TRequest, TResponse> 
    where TRequest : class, IRequest<TResponse>
{
    private readonly IHandle<TRequest, TResponse> _decoratedHandler;
    private readonly LifetimeScopeTester _lifetimeScopeTester;

    public SecondDecoratorHandler(IHandle<TRequest, TResponse> decoratedHandler, LifetimeScopeTester lifetimeScopeTester)
    {
        _decoratedHandler = decoratedHandler;
        _lifetimeScopeTester = lifetimeScopeTester;
    }

    public TResponse Handle(TRequest request)
    {
        Console.WriteLine("SecondDecoratorHandler - LifetimeScopeTester[{0}]", _lifetimeScopeTester.GetHashCode());
        return _decoratedHandler.Handle(request);
    }
}

如您所见,我将pipleine包裹在一个名为pipeline的作用域中,这意味着每次我将LifetimeScopeTester定义为pipeline的作用域时,都会得到相同的实例.

As you can see, I wrap the pipleine in a scope named pipeline which means that everytime I resolve LifetimeScopeTester which is scope to pipeline, I get the same instance.

我以为我可以取代

using(var scope = container.BeginLifetimeScope("pipline")) {
    var pingHandler = scope.Resolve<IHandle<PingRequest, PingResponse>>();
    pingHandler.Handle(new PingRequest());
}

使用

var pingHandler = scope.Resolve<IHandle<PingRequest, PingResponse>>();
pingHandler.Handle(new PingRequest());

通过创建另一个执行相同操作的装饰器.

by creating another decorator that does that same thing.

我的第一个直觉是:

public class LifetimeScopeDecoratorHandler<TRequest, TResponse> : IHandle<TRequest, TResponse> 
    where TRequest : class, IRequest<TResponse>
{
    private readonly ILifetimeScope _scope;
    private readonly IHandle<TRequest, TResponse> _decoratedHandler;

    public LifetimeScopeDecoratorHandlerAttempt1(ILifetimeScope scope, 
        IHandle<TRequest, TResponse> decoratedHandler)
    {
        _scope = scope;
        _decoratedHandler = decoratedHandler;
    }

    public TResponse Handle(TRequest request)
    {
        Console.WriteLine("LifetimeScopeDecoratorHandler");

        TResponse response;

        using (_scope.BeginLifetimeScope("pipeline"))
        {
            response = _decoratedHandler.Handle(request);
        }

        return response;
    }
}

但是decoratedHandler在注入时将已经解决,因此将无法工作.

But the decoratedHandler would have already been resolved by the time it's injected so that won't work.

所以我尝试了:

public class LifetimeScopeHandler<TRequest, TResponse> : IHandle<TRequest, TResponse> 
    where TRequest : class, IRequest<TResponse>
{
    private readonly ILifetimeScope _scope;
    private readonly Func<IHandle<TRequest, TResponse>> _decoratedHandlerFactory;

    public LifetimeScopeHandler(ILifetimeScope scope, 
        Func<IHandle<TRequest, TResponse>> decoratedHandlerFactory)
    {
        _scope = scope;
        _decoratedHandlerFactory = decoratedHandlerFactory;
    }

    public TResponse Handle(TRequest request)
    {
        Console.WriteLine("LifetimeScopeDecoratorHandler");

        TResponse response;

        using (_scope.BeginLifetimeScope("pipeline"))
        {
            var decoratedHandler = _decoratedHandlerFactory();
            response = decoratedHandler.Handle(request);
        }

        return response;
    }
}

但是,这在调用_decoratedHandlerFactory()时会无限重复,尝试再次用LifetimeScopeHandler装饰器包装内部处理程序.

However this repeated infinitely as calling _decoratedHandlerFactory() tries to wrap the inner handler with a LifetimeScopeHandler decorator again.

这是我想要实现的目标.

Is what I'm trying to achieve possible.

我在 https://dotnetfiddle.net/hwujNI 上创建了一个dotnetfiddle,展示了问题.

I have created a dotnetfiddle at https://dotnetfiddle.net/hwujNI demonstrating the issue.

推荐答案

LifetimeScopeHandler类的Handle方法调用decoratedHandlerFactory委托时,它要求 Autofac 解析LifetimeScopeHandler.这就是为什么您有StackOverflowException的原因.我们可以将您的案例简化为以下代码示例:

When the Handle method of LifetimeScopeHandler class invoke the decoratedHandlerFactory delegate, it asks Autofac to resolve a IHandle<TRequest, TResponse> which is a LifetimeScopeHandler. That's why you have a StackOverflowException. We can simplify your case to this code sample :

public class Foo
{
    public Foo(Func<Foo> fooFactory)
    {
        this._fooFactory = fooFactory;
    }
    private readonly Func<Foo> _fooFactory;

    public void Do()
    {
        Foo f = this._fooFactory();
        f.Do();
    }
}

即使只有一个Foo实例,您也会有一个StackOverflowException

Even if there is a single instance of Foo you will have a StackOverflowException

为了解决此问题,您必须指示 Autofac ,其中LifetimeScopeHandlerdecoratedHandlerFactory委托不应是LifetimeScopeHandler的委托.

In order to resolve this issue, you have to indicate Autofac that the decoratedHandlerFactory delegate of LifetimeScopeHandler should not be a delegate of LifetimeScopeHandler.

您可以使用WithParameter来指示使用特定参数的最后一个修饰符:

You can use the WithParameter to indicate the last decorator to use a specific parameter :

builder.RegisterGenericDecorator(
    typeof(LifetimeScopeHandler<,>),
    typeof(IHandle<,>),
    "FirstDecoratorHandler"
)
.WithParameter((pi, c) => pi.Name == "decoratedHandlerFactory",
               (pi, c) => c.ResolveKeyed("FirstDecoratorHandler", pi.ParameterType))
.As(typeof(IHandle<,>));

使用此配置,输出将为

LifetimeScopeHandler
FirstDecoratorHandler-LifetimeScopeTester [52243212]
SecondDecoratorHandler-LifetimeScopeTester [52243212]
PingHandler

LifetimeScopeHandler
FirstDecoratorHandler - LifetimeScopeTester[52243212]
SecondDecoratorHandler - LifetimeScopeTester[52243212]
PingHandler

顺便说一句,您希望LifetimeScopeHandler是一种特殊的装饰器,它将在特殊的范围内创建内部的IHandler<,>.

By the way, you want LifetimeScopeHandler to be a special kind of decorator that will create the inner IHandler<,> in a special scope.

您可以通过要求LifetimeScopeHandler为您创建正确的作用域并解决先前的Ihandler来做到这一点.

You can do this by asking the LifetimeScopeHandler to create the correct scope for you and resolve the previous Ihandler.

public class LifetimeScopeHandler<TRequest, TResponse> 
    : IHandle<TRequest, TResponse> where TRequest : class, IRequest<TResponse>
{
    private readonly ILifetimeScope _scope;

    public LifetimeScopeHandler(ILifetimeScope scope)
    {
        this._scope = scope;
    }

    public TResponse Handle(TRequest request)
    {
        Console.WriteLine("LifetimeScopeDecoratorHandler");

        using (ILifetimeScope s = this._scope.BeginLifetimeScope("pipline"))
        {
            var decoratedHandler = 
                s.ResolveKeyed<IHandle<TRequest, TResponse>>("FirstDecoratorHandler");
            TResponse response = decoratedHandler.Handle(request);
            return response;
        }
    }
}

此实现将要求LifetimeScopeHandler知道链上的第一个装饰器,我们可以通过在其构造器上发送名称来绕过该装饰器.

This implementation will require that LifetimeScopeHandler knows the first decorator on the chain, we can bypass that by sending the name on its constructor.

public class LifetimeScopeHandler<TRequest, TResponse> 
    : IHandle<TRequest, TResponse> where TRequest : class, IRequest<TResponse>
{
    private readonly ILifetimeScope _scope;
    private readonly String _previousHandlerName; 

    public LifetimeScopeHandler(ILifetimeScope scope, String previousHandlerName)
    {
        this._scope = scope;
        this._previousHandlerName = previousHandlerName; 
    }

    public TResponse Handle(TRequest request)
    {
        Console.WriteLine("LifetimeScopeDecoratorHandler");

        using (ILifetimeScope s = this._scope.BeginLifetimeScope("pipline"))
        {
            var decoratedHandler = 
                s.ResolveKeyed<IHandle<TRequest, TResponse>>(previousHandlerName);
            TResponse response = decoratedHandler.Handle(request);
            return response;
        }
    }
}

您将必须像这样注册它:

And you will have to register it like this :

builder.RegisterGenericDecorator(
         typeof(LifetimeScopeHandler<,>),
         typeof(IHandle<,>),
         "FirstDecoratorHandler"
       )
       .WithParameter("previousHandlerName", "FirstDecoratorHandler")
       .As(typeof(IHandle<,>));

我们也可以不使用RegisterGenericDecorator方法来绕过所有内容.

We can also bypass everything by not using the RegisterGenericDecorator method.

如果我们这样注册LifetimeScopeHandler:

builder.RegisterGeneric(typeof(LifetimeScopeHandler<,>))
       .WithParameter((pi, c) => pi.Name == "decoratedHandler",
                      (pi, c) =>
                      {
                          ILifetimeScope scope = c.Resolve<ILifetimeScope>();
                          ILifetimeScope piplineScope = scope.BeginLifetimeScope("pipline");
                          var o = piplineScope.ResolveKeyed("FirstDecoratorHandler", pi.ParameterType);
                          scope.Disposer.AddInstanceForDisposal(piplineScope);
                          return o;
                      })
       .As(typeof(IHandle<,>));

LifetimeScopeHandler现在可以看起来像所有装饰器:

And LifetimeScopeHandler can now look like all decorator :

public class LifetimeScopeHandler<TRequest, TResponse> 
  : IHandle<TRequest, TResponse> where TRequest : class, IRequest<TResponse>
{
    private readonly IHandle<TRequest, TResponse> _decoratedHandler;

    public LifetimeScopeHandler(IHandle<TRequest, TResponse> decoratedHandler)
    {
        this._decoratedHandler = decoratedHandler;
    }

    public TResponse Handle(TRequest request)
    {
        Console.WriteLine("LifetimeScopeDecoratorHandler");

        TResponse response = this._decoratedHandler.Handle(request);
        return response;
    }
}

顺便说一句,如果您在一个范围内使用多个IHandler<,>并且您需要一个单一的pipline范围,则此解决方案可能会出现问题.要解决此问题,您可以看到以下dotnetfiddle: https://dotnetfiddle.net/rQgy2X ,但在我看来复杂,您可能不需要它.

By the way, this solution may have an issue if you use more than one IHandler<,> in a scope and you need to have a single pipline scope. To resolve this, you can see this dotnetfiddle : https://dotnetfiddle.net/rQgy2X but it seems to me over complicated and you may not need it.

这篇关于Autofac终身范围装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆