尝试从IInterceptor中的Ninject解析项目失败

本文关键字:项目 失败 Ninject 中的 IInterceptor | 更新日期: 2023-09-27 18:01:27

我正在尝试在我当前的项目中使用Ninject,到目前为止,我一直很喜欢它。我正在尝试配置一个IInterceptor对象来拦截和处理对我的服务层的失败方法调用。它托管在ASP.NET MVC 5应用程序中。

IInterceptor中,我尝试了几种方法,例如:

  1. 使用构造函数注入设置私有变量,但发现Ninject似乎会无限期地为方法重用IInterceptor实例,我还没有找到阻止这种情况的方法。由于我引入的范围之一是DbContext,它会在其他地方被处理,因此它最终会在未来的任何请求中失败,而不是在创建请求时失败

  2. 我发现IInvocation具有Request.Kernel属性。但是,当我尝试从容器(即.InRequestScope()(解析我的UOW时,它失败了,因为它尝试解析IUowService依赖项(其中一个依赖项依赖于HttpContext,此时HttpContext为null(,但似乎是在请求范围之外进行的。它忽略了这样一个事实,即它需要的依赖项已经在ASP.NET请求中创建,并试图创建新的依赖项。

  3. 为拦截器this.Bind<NinjectExceptionHandler>().ToSelf().InTransientScope()设置绑定,但这似乎并没有停止拦截器的缓存。

我想我错过了什么。我理解想要缓存IInterceptor对象以提高性能,但我发现无法轻松使用IOC容器或Injection来获得请求所需的对象,这让我很恼火。

这是我遇到的最后一个问题,我需要拦截并运行,所以非常感谢任何帮助!

尝试从IInterceptor中的Ninject解析项目失败

根据您的请求,我将详细介绍我们是如何实现"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将获得两个拦截器:LoggingInterceptorSomeOtherInterceptor

以及提供者的实现:

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的IAdviceRegistryIAdvice方法当然更好(但也需要更多地了解ninject的工作原理(。

因此,似乎没有办法用Ninject优雅地完成我尝试的操作。一旦进入IInterceptor和异步操作的后期,HttpContext就丢失了,Ninject无法解决它本应认为在范围内的事情。再加上它重用IInterceptor作为一种方法(就像我说的,可以理解,但令人恼火(,我就是无法让它按我想要的方式正常工作

为了避开这个事实,我所能做的是一些简单但有点笨拙的事情(我认为(。由于我拦截的所有方法都在我的服务层中,并且我所有的服务都通过BaseService抽象基类实现了IBaseService,而这个抽象基类恰好有我需要的对象作为属性,所以我能够在拦截器中做到这一点:

var uow = (invocation.Request.Target as IBaseService).UnitOfWork;

这允许我访问我的工作单元并使其失败,以及访问我正在处理的日志实例

虽然这是可行的,但我希望看到一些方法,通过多次调用,或者进一步调用内核,使拦截器构造函数注入正确工作,以意识到它已经解析了一个仍在作用域中的对象(尽管我猜测它可能认为它超出了作用域,因为ASP.Net在等待时放弃了作用域(。

对于任何感兴趣的人,我会很快尝试在我的博客上发布这件事(如果真的感兴趣,请查看我的用户页面,而不是我自己垃圾邮件(。