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);
}
}
如您所见,我将管道包装在一个名为 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
class 的 Handle
方法调用 decoratedHandlerFactory
委托时,它要求 Autofac 解析一个 IHandle<TRequest, TResponse>
这是一个 LifetimeScopeHandler
。这就是为什么你有一个 WhosebugException
。我们可以将您的案例简化为此代码示例:
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
实例,您也会有一个 WhosebugException
为了解决这个问题,您必须指出 Autofac LifetimeScopeHandler
的 decoratedHandlerFactory
委托不应该是 [=18 的委托=].
您可以使用 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 但在我看来它过于复杂,您可能不需要它。
我正在使用 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);
}
}
如您所见,我将管道包装在一个名为 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
class 的 Handle
方法调用 decoratedHandlerFactory
委托时,它要求 Autofac 解析一个 IHandle<TRequest, TResponse>
这是一个 LifetimeScopeHandler
。这就是为什么你有一个 WhosebugException
。我们可以将您的案例简化为此代码示例:
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
实例,您也会有一个 WhosebugException
为了解决这个问题,您必须指出 Autofac LifetimeScopeHandler
的 decoratedHandlerFactory
委托不应该是 [=18 的委托=].
您可以使用 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 但在我看来它过于复杂,您可能不需要它。