Interceptor: Ninject --> Simple Injector

Oct 28, 2013 at 3:28 AM
Edited Oct 28, 2013 at 3:29 AM
Hi Dot_Net_Junkie,

First of all thank you for an awesome DI framework! I'm blown away by the performance compared to Ninject!

After doing a POC to management I've been tasked to replace Ninject with Simple Injector across our full application stack. One thing I am struggling with is getting the IInterceptor implementation to register correctly.

I'm using the following packages:

<package id="SimpleInjector" version="2.3.6" targetFramework="net40" />
<package id="SimpleInjector.Integration.Wcf" version="2.3.6" targetFramework="net40" />
<package id="SimpleInjector.Integration.Wcf.QuickStart" version="2.3.6" targetFramework="net40" />
<package id="SimpleInjector.Packaging" version="2.3.6" targetFramework="net40" />

My interceptor is defined as follows:
   public class ExceptionLoggerInterceptor : IInterceptor
    {
        /// <summary>
        /// Gets the log for this object.
        /// </summary>
        private ILog m_log = LogManager.GetLogger(typeof(ExceptionLoggerInterceptor));
        private bool m_hasError;

        /// <summary>
        /// Initializes a new instance of the <see cref="ExceptionLoggerInterceptor"/> class.
        /// </summary>
        public ExceptionLoggerInterceptor()
        {
            m_hasError = false;
        }

        /// <summary>
        /// Intercept invocation
        /// </summary>
        /// <param name="invocation">IInvocation</param>
        public virtual void Intercept(IInvocation invocation)
        {
            try
            {
                BeforeInvoke(invocation);
                invocation.Proceed();
            }
            catch (Exception ex)
            {
                OnError(invocation, ex);
            }
            finally
            {
                AfterInvoke(invocation);
            }
        }

        /// <summary>
        /// Override before invoke
        /// </summary>
        /// <param name="invocation">IInvocation</param>
        protected void BeforeInvoke(IInvocation invocation)
        {
            m_hasError = false;

            if (m_log.IsDebugEnabled)
            {
                //log something
            }
        }

        /// <summary>
        /// Overrides base.OnError and logs error
        /// </summary>
        /// <param name="invocation">IInvocation</param>
        /// <param name="exception">Exception</param>
        protected void OnError(IInvocation invocation, Exception exception)
        {
            //log error
        }

        /// <summary>
        /// Override AfterInvoke
        /// </summary>
        /// <param name="invocation">IInvocation</param>
        protected void AfterInvoke(IInvocation invocation)
        {
            if (m_log.IsDebugEnabled)
            {
               //Log something
            }

            m_hasError = false;
        }
    }
Simple Injector Initializer is defined as follows:
   public static class SimpleInjectorInitializer
    {
        public static void Initialize()
        {
            var container = new Container();
            
            InitializeContainer(container);

            container.RegisterWithContext<ILog>(dependencyContext =>
            {
                Type type = dependencyContext.ImplementationType ?? typeof(object); //Nothing to see here - move right along :-) ImplementationType  is null at times - not sure why.
                return (ILog)LogManager.GetLogger(type);
            });

            //container.Register<IInterceptor,ExceptionLoggerInterceptor>();
            container.InterceptWith<ExceptionLoggerInterceptor>(x => x.IsInterface);

            SimpleInjectorServiceHostFactory.SetContainer(container);

            container.ResolveUnregisteredType += (s, e) =>
            {
                if (!e.Handled && !e.UnregisteredServiceType.IsAbstract)
                {
                    throw new Exception(e.UnregisteredServiceType.Name +
                        " is a concrete unregistered type.");
                }
                else
                {
                    throw new Exception(e.UnregisteredServiceType.Name +
                        " is an unregistered type.");
                }
            };

            container.Verify();
        }

        private static void InitializeContainer(Container container)
        {
            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");

            var assemblies =
                from file in Directory.GetFiles(path, "myAssemblies*.dll", SearchOption.AllDirectories)
                select Assembly.LoadFile(file);


            container.EnablePerWcfOperationLifestyle();
            container.RegisterWcfServices(assemblies.ToArray());
            container.RegisterPackages(assemblies);
        }
    }
When loading up a WSDL the following error is then thrown:
ExceptionLoggerInterceptor is a concrete unregistered type.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack > trace for more information about the error and where it originated in the code.

Exception Details: System.Exception: ExceptionLoggerInterceptor is a concrete unregistered type.

Source Error:
Line 45: if (!e.Handled && !e.UnregisteredServiceType.IsAbstract)
Line 46: {
Line 47: throw new Exception(e.UnregisteredServiceType.Name +
Line 48: " is a concrete unregistered type.");
Line 49: }
My Ninject binding looks as follows;
            //Kernel.Bind(x => x
            //    .FromAssembliesMatching("myAssemblies*.dll")
            //    .SelectAllClasses()
            //    .BindToSelf()
            //    .Configure(y => y.Intercept().With(new ExceptionLoggerInterceptor()).InOrder(1)));
I must be missing something obvious here...any pearls of wisdom to guide this layman in the correct direction?

Cheers
Chavez
Coordinator
Oct 28, 2013 at 7:58 AM
Edited Oct 28, 2013 at 10:26 AM
You made the following registration:
container.Register<IInterceptor,ExceptionLoggerInterceptor>();
This means that you registered an IInterceptor. The InterceptWith however requests the supplied interceptor type from the container. But the container doesn't know anything about ExceptionLoggerInterceptor, but only about IInterceptor.

So you either have t change your cnfiguration to:
container.Register<ExceptionLoggerInterceptor>();
Or:
container.InterceptWith<IInterceptor>(x => x.IsInterface);
He first solution is better since changing the InterceptWith disallows you to add a second interceptor.
Coordinator
Oct 28, 2013 at 10:33 AM
Edited Oct 28, 2013 at 10:26 PM
I hope you don't mind giving me some feedback on your code, but your solution looks really scary to me. What I noticed is that you:
  1. Apply the ExceptionLoggerInterceptor to all interfaces in the system, and
  2. The ExceptionLoggerInterceptor never lets any exception bubble up the call stack, but will do a catch-and-continue.
This is scary, because with this approach you effectively recreated the old VBScript "On Error Resume Next" mode, where the system is allowed to continue after an error is occurred. This is typically a bad idea, even for systems that must be fault tolerant. I think you should reconsider this strategy, since it will only make things worse. Please take a look at this Stackoverflow Q&A.

> ImplementationType is null at times - not sure why.

The ServiceType and ImplementationType properties will return null values when the requested type is a root type. In case of a root type (i.e. when you call container.GetInstance<SomeType>(), in that case SomeType is a root type, since it isn’t injected into anything) there is no parent object and therefore those values will be null.
Marked as answer by dot_NET_Junkie on 2/26/2014 at 1:21 PM
Oct 29, 2013 at 2:45 AM
Hi there, thanks for the reply.

The following resolved the issue as per your suggestion:
            container.Register<ExceptionLoggerInterceptor>();
            container.InterceptWith<ExceptionLoggerInterceptor>(x => x.IsInterface);
In regards to your other two questions:
Apply the ExceptionLoggerInterceptor to all interfaces in the system, and
The ExceptionLoggerInterceptor never lets any exception bubble up the call stack, but will do a catch-and-continue.
  1. The code will be changed to only intercept the Service and Repository layers within the given assemblies. As I was focused on getting the interceptor to work I wasn't concerned about what's being intercepted at that point.
  2. The exception is indeed bubbled up and not swallowed as suggested. I merely commented out irrelevant code as it has no baring on the issue at hand.
    The point of catching the exception in the interceptor is to log the Service / Repository methods input parameters to aid in PROD support.
Currently through Ninjects IInterceptor we log the following on an exception:
        _log.Error(string.Format("Errors on Method execution: [{0}.{1}], Parameters: [{2}]", invocation.Request.Method.DeclaringType.FullName, ((System.Reflection.MemberInfo)(invocation.Request.Method)).Name, InterceptorHelper.SerializeArguments(invocation.Request.Arguments)), exception);