Autofac终身范围装饰器
我正在使用Autofac实现一个命令处理程序模式,并使用它的decorator工具处理交叉切割问题,如日志记录,身份validation等。
我还有依赖项,我只希望作用于请求/响应管道的生命周期。
我在下面有一个示例实现:
public class Program { public static void Main() { var builder = new ContainerBuilder(); builder.RegisterAssemblyModules(typeof(HandlerModule).Assembly); builder.RegisterType().AsSelf() .InstancePerMatchingLifetimeScope("pipline"); var container = builder.Build(); using(var scope = container.BeginLifetimeScope("pipline")) { var pingHandler = scope.Resolve<IHandle>(); 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 where TRequest : class, IRequest { TResponse Handle(TRequest request); } public interface IRequest { } public class PingRequest : IRequest { } public class PingResponse { } public class PingHandler : IHandle { public PingResponse Handle(PingRequest request) { Console.WriteLine("PingHandler"); return new PingResponse(); } } public class FirstDecoratorHandler : IHandle where TRequest : class, IRequest { private readonly IHandle _decoratedHandler; private readonly LifetimeScopeTester _lifetimeScopeTester; public FirstDecoratorHandler(IHandle 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 : IHandle where TRequest : class, IRequest { private readonly IHandle _decoratedHandler; private readonly LifetimeScopeTester _lifetimeScopeTester; public SecondDecoratorHandler(IHandle 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>(); pingHandler.Handle(new PingRequest()); }
同
var pingHandler = scope.Resolve<IHandle>(); pingHandler.Handle(new PingRequest());
通过创建另一个做同样事情的装饰器。
我的第一直觉是:
public class LifetimeScopeDecoratorHandler : IHandle where TRequest : class, IRequest { private readonly ILifetimeScope _scope; private readonly IHandle _decoratedHandler; public LifetimeScopeDecoratorHandlerAttempt1(ILifetimeScope scope, IHandle 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 : IHandle where TRequest : class, IRequest { private readonly ILifetimeScope _scope; private readonly Func<IHandle> _decoratedHandlerFactory; public LifetimeScopeHandler(ILifetimeScope scope, Func<IHandle> 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
,这是一个LifetimeScopeHandler
。 这就是为什么你有一个StackOverflowException
。 我们可以将您的案例简化为此代码示例:
public class Foo { public Foo(Func fooFactory) { this._fooFactory = fooFactory; } private readonly Func _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 : IHandle where TRequest : class, IRequest { 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>("FirstDecoratorHandler"); TResponse response = decoratedHandler.Handle(request); return response; } } }
此实现将要求LifetimeScopeHandler
知道链上的第一个装饰器,我们可以通过在其构造函数上发送名称来绕过它。
public class LifetimeScopeHandler : IHandle where TRequest : class, IRequest { 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>(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 piplineScope = scope.BeginLifetimeScope("pipline"); var o = piplineScope.ResolveKeyed("FirstDecoratorHandler", pi.ParameterType); scope.Disposer.AddInstanceForDisposal(piplineScope); return o; }) .As(typeof(IHandle<,>));
而LifetimeScopeHandler
现在看起来像所有装饰器:
public class LifetimeScopeHandler : IHandle where TRequest : class, IRequest { private readonly IHandle _decoratedHandler; public LifetimeScopeHandler(IHandle 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。 如何使用自定义方法(属性)来解决一些接口?
- autofac的Func 来解析命名服务
- 确定解析实例的依赖关系 – IoC(autofac)
- 插件架构中的DI(Autofac):每个插件有一个单独的DI容器吗?
- WebApi,Autofac,System.Web.Http.Filters.ActionFilterAttribute每个请求的实例
- 是否可以从Autofac容器构建器中删除现有注册?
- 为参数的具体名称注册字符串值
- 如何解决Autofac InstancePerHttpRequest
- .NET 4,AllowPartiallyTrustedCallers属性和SecurityCritical等安全标记