0

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

在 中IInterceptor,我尝试了几件事,例如:

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

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

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

我想我缺少一些东西。我理解想要缓存IInterceptor对象以提高性能,但我发现我无法轻松使用 IOC 容器或注入来获取我的请求所需的对象,这很令人讨厌。

这是我在需要时启动和运行拦截的最后一个问题,因此非常感谢任何帮助!

4

2 回答 2

1

根据您的要求,我将详细介绍我们如何实现“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 的方法和它IAdviceRegistry当然IAdvice更好(但也需要更深入地了解 ninject 的工作原理)。

于 2014-05-04T13:47:25.663 回答
0

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

我能够绕过这个事实的方法很简单,但有点笨拙(我认为)。由于我拦截的所有方法都在我的服务层中,并且我的所有服务都IBaseService通过一个BaseService抽象基类实现了一个,它恰好具有我需要的对象作为属性,所以我能够在拦截器中执行此操作:

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

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

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

对于任何感兴趣的人,我将尽快尝试在我的博客上发布相关内容(如果真的感兴趣,请查看我的用户页面,而不是我自己发送垃圾邮件)。

于 2014-05-04T12:38:45.563 回答