尝试从IInterceptor中的Ninject解析项目失败
本文关键字:项目 失败 Ninject 中的 IInterceptor | 更新日期: 2023-09-27 18:01:27
我正在尝试在我当前的项目中使用Ninject,到目前为止,我一直很喜欢它。我正在尝试配置一个IInterceptor对象来拦截和处理对我的服务层的失败方法调用。它托管在ASP.NET MVC 5应用程序中。
在IInterceptor
中,我尝试了几种方法,例如:
-
使用构造函数注入设置私有变量,但发现Ninject似乎会无限期地为方法重用
IInterceptor
实例,我还没有找到阻止这种情况的方法。由于我引入的范围之一是DbContext
,它会在其他地方被处理,因此它最终会在未来的任何请求中失败,而不是在创建请求时失败 -
我发现
IInvocation
具有Request.Kernel
属性。但是,当我尝试从容器(即.InRequestScope()
(解析我的UOW时,它失败了,因为它尝试解析IUowService
依赖项(其中一个依赖项依赖于HttpContext,此时HttpContext为null(,但似乎是在请求范围之外进行的。它忽略了这样一个事实,即它需要的依赖项已经在ASP.NET请求中创建,并试图创建新的依赖项。 -
为拦截器
this.Bind<NinjectExceptionHandler>().ToSelf().InTransientScope()
设置绑定,但这似乎并没有停止拦截器的缓存。
我想我错过了什么。我理解想要缓存IInterceptor
对象以提高性能,但我发现无法轻松使用IOC容器或Injection来获得请求所需的对象,这让我很恼火。
这是我遇到的最后一个问题,我需要拦截并运行,所以非常感谢任何帮助!
根据您的请求,我将详细介绍我们是如何实现"1个代理:1个拦截器"实例关系的。
我们采取了一种简单的方式,它不像官方的ninject拦截扩展提供的那样灵活。我们直接依赖castle.core动态代理,因此也依赖castle的IInvocation
接口。
(请注意,下面的代码不是针对没有目标的代理,但有目标的代理非常相似——唯一改变的是,您需要知道目标类类型,并使用IResolutionRoot.Get<TargetClassType>()
来初始化它(。
基本上,我们创建了一个类似于的绑定
IBindingRoot.Bind<IFoo>()
.ToProvider<InterfaceProxyWithoutTargetProvider<IFoo>>();
现在我们当然需要知道代理将使用哪些拦截器。我们再次使用了一种简单但不太好的设计:
public interface IInterceptorBindingDefinition<TTarget>
{
Type InterceptorType { get; }
}
public class InterceptorBindingDefinition<TTarget, TInterceptor> : IInterceptorBindingDefinition<TTarget>
where TInterceptor : IInterceptor
{
Type InterceptorType { get { return typeof(TInterceptor); } }
}
IBindingRoot
.Bind<IInterceptorBindingDefinition<IFoo>>()
.To<InterceptorBindingDefinition<TTarget, LoggingInterceptor>();
IBindingRoot
.Bind<IInterceptorBindingDefinition<IFoo>>()
.To<InterceptorBindingDefinition<TTarget, SomeOtherInterceptor>();
这意味着IFoo
将获得两个拦截器:LoggingInterceptor
和SomeOtherInterceptor
。
以及提供者的实现:
public class InterfaceProxyWithoutTargetProvider<TInterface> : IProvider<TInterface>
where TInterface : class
{
private readonly IProxyGenerator proxyGenerator;
private readonly IInterceptorFactory interceptorFactory;
public InterfaceProxyWithoutTargetProvider(IProxyGenerator proxyGenerator, IInterceptorFactory interceptorFactory)
{
this.proxyGenerator = proxyGenerator;
this.interceptorFactory = interceptorFactory;
}
public Type Type
{
get { return typeof(TInterface); }
}
public object Create(IContext context)
{
var interceptorTypes = context.Kernel.Get<IEnumerable<IInterceptorBindingDefinition<TInterface>>();
IList<IInterceptor> interceptors = interceptorTypes
.Select(x => x.InterceptorType)
.Select(x => context.ContextPreservingGet(x))
.ToList();
return this.proxyGenerator.CreateInterfaceProxyWithoutTarget<TInterface>(interceptors);
}
}
当然,现在我们对它进行了一点改进,这样我们就有了一个流畅的语法来配置代理和拦截器的绑定——这很容易。
然而,ninject.extensions.interception的IAdviceRegistry
和IAdvice
方法当然更好(但也需要更多地了解ninject的工作原理(。
因此,似乎没有办法用Ninject优雅地完成我尝试的操作。一旦进入IInterceptor和异步操作的后期,HttpContext就丢失了,Ninject无法解决它本应认为在范围内的事情。再加上它重用IInterceptor
作为一种方法(就像我说的,可以理解,但令人恼火(,我就是无法让它按我想要的方式正常工作
为了避开这个事实,我所能做的是一些简单但有点笨拙的事情(我认为(。由于我拦截的所有方法都在我的服务层中,并且我所有的服务都通过BaseService
抽象基类实现了IBaseService
,而这个抽象基类恰好有我需要的对象作为属性,所以我能够在拦截器中做到这一点:
var uow = (invocation.Request.Target as IBaseService).UnitOfWork;
这允许我访问我的工作单元并使其失败,以及访问我正在处理的日志实例
虽然这是可行的,但我希望看到一些方法,通过多次调用,或者进一步调用内核,使拦截器构造函数注入正确工作,以意识到它已经解析了一个仍在作用域中的对象(尽管我猜测它可能认为它超出了作用域,因为ASP.Net在等待时放弃了作用域(。
对于任何感兴趣的人,我会很快尝试在我的博客上发布这件事(如果真的感兴趣,请查看我的用户页面,而不是我自己垃圾邮件(。