[英]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
的范圍內,這意味着每當我解析LifetimeScopeTester
( pipeline
范圍)時,我都會得到相同的實例。
我認為我可以替換
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
為了解決此問題,您必須指明Autofac , LifetimeScopeHandler
的decoratedHandlerFactory
委托不應該是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.