簡體   English   中英

Autofac終身范圍裝飾器

[英]Autofac Lifetime Scope Decorator

我正在使用Autofac實現命令處理程序模式,並使用它的裝飾器工具處理交叉切割問題,如日志記錄,身份驗證等。

我還有依賴項,我只希望作用於請求/響應管道的生命周期。

我在下面有一個示例實現:

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的范圍內,這意味着每當我解析LifetimeScopeTesterpipeline范圍)時,我都會得到相同的實例。

我認為我可以替換

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());

通過創建另一個做同樣事情的裝飾器。

我的第一直覺是:

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漢德勒已經解決了,那將無法解決。

所以我嘗試過:

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裝飾器包裝內部處理程序時,這無限重復。

是我正在努力實現的目標。

我在https://dotnetfiddle.net/hwujNI上創建了一個dotnetfiddle來演示這個問題。

LifetimeScopeHandler類的Handle方法調用decoratedHandlerFactory委托時,它要求Autofac解析IHandle<TRequest, TResponse> ,這是一個LifetimeScopeHandler 這就是你有StackOverflowException的原因。 我們可以將您的案例簡化為此代碼示例:

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

為了解決此問題,您必須指明AutofacLifetimeScopeHandlerdecoratedHandlerFactory委托不應該是LifetimeScopeHandler的委托。

您可以使用WithParameter指示使用特定參數的最后一個裝飾器:

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是一種特殊的裝飾器,它將在特殊范圍內創建內部IHandler<,>

您可以通過要求LifetimeScopeHandler為您創建正確的范圍並解析之前的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知道鏈上的第一個裝飾器,我們可以通過在其構造函數上發送名稱來繞過它。

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;
        }
    }
}

你必須這樣注冊:

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

我們也可以通過不使用RegisterGenericDecorator方法繞過一切。

如果我們像這樣注冊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現在看起來像所有裝飾器:

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,但在我看來這很復雜,你可能不需要它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM