I am attempting to use Ninject on my current project, and up to now, have been loving it. I am in the middle of attempting to configure an IInterceptor object to intercept and handle a failed method call to my service layer. This is hosted in an ASP.NET MVC 5 application.

In the IInterceptor, I've tried several things, such as:

  1. Setting private variables using constructor injection, but came to discover that it appears Ninject will reuse an IInterceptor instance for a method indefinitely, and I haven't found a way to stop that. Since one of the things I bring into scope is a DbContext which gets disposed elsewhere, it ends up failing on any future requests than the one it was created on.

  2. I found that the IInvocation has a Request.Kernel property. However, when I attempt to resolve my UOW from the container, which is .InRequestScope(), it fails, since it attempts to resolve the IUowService dependencies, (one of the dependencies depends on the HttpContext which is null at this point), but appears to be doing so outside the Request scope. It is ignoring the fact that the dependencies it needs have already been created within the ASP.NET request, and is attempting to create new ones.

  3. Setting a binding for the interceptor this.Bind<NinjectExceptionHandler>().ToSelf().InTransientScope(), yet this didn't seem to stop the caching of the interceptor.

I imagine there is something I am missing. I understand wanting to cache IInterceptor objects for performance, but I find it irksome that I can't easily use the IOC container or Injection to get the objects I need for my request.

This is the last issue I am having with getting interception up and running as I need, so any help is greatly appreciated!

有帮助吗?

解决方案 2

So it appears that there is no way to do what I was trying gracefully with Ninject. Once in the IInterceptor and in the later parts of async operations, the HttpContext was lost and Ninject couldn't resolve things that really it should have thought were in scope. Coupled with the fact that it reused IInterceptor's for a method (like I said, understandable, but irritating), I just couldn't get it to work right as I wanted to.

What I was able to do to get around the fact was something simple, yet a little kludgy (I think). Since all the methods that I was intercepting were in my service layer, and all my services implemented a IBaseService through a BaseService abstract base class, which happened to have the objects I needed as properties, I was able to do this in the interceptor:

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

This allowed me to access my unit of work and Fail it, as well as access the logging instance I was working on.

While this works, I would like to see someway to get interceptor constructor injection working correctly through multiple calls, or calls to the Kernel further down the line to realize that it has already resolved an object still in scope (although I am guessing that it may think its out of scope since ASP.Net abandoned the scope upon await).

For any interested, I am going to try and post about this on my blog soon (see my user page if actually interested, not spamming SO myself).

其他提示

Per your request i'm going into more detail on how we've achieved "1 proxy : 1 interceptor" instance relation ship.

We've taken the easy way which does not offer as much flexibility as what the official ninject interception extensions offers. We are relying directly on castle.core dynamic proxy and thus castle's IInvocation interface.

(Please not the code below is for a proxy without target, but a proxy with target is quite similar -- the only thing which changes is that you'll need to know the target class type and use IResolutionRoot.Get<TargetClassType>() to instanciate it).

Basically we created a binding like:

IBindingRoot.Bind<IFoo>()
     .ToProvider<InterfaceProxyWithoutTargetProvider<IFoo>>();

Now of course we need to know which interceptors the proxy shall use. Again we are using an easy - and not so nice - design:

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>();      

This means IFoo shall get two interceptors: LoggingInterceptor and SomeOtherInterceptor.

and the implementation of the provider:

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);
    }
}

Now of course we polished the thing a little bit so we have a fluent syntax configuring the binding of the proxy and the interceptor - which is easy enough.

However ninject.extensions.interception's approach with its IAdviceRegistry and IAdvice is certainly better (but also requires more insight into how ninject works).

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top