Search code examples
c#autofacautofac-module

Autofac log4net middleware example does not seem to work


I am trying to start using middleware and creating a logger for a project and thought the autofac log4net example was a good starting point as the project already is making use of autofac, but I am not being able to do so. I have the following classes:

using System;
using System.Linq;
using System.Reflection;
using Autofac;
using Autofac.Core;
using Autofac.Core.Registration;
using Autofac.Core.Resolving.Pipeline;
using log4net;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestProject1
{
    public class TestMiddlewareModule : Autofac.Module
    {
        private readonly IResolveMiddleware middleware;

        public TestMiddlewareModule(IResolveMiddleware middleware)
        {
            this.middleware = middleware;
        }

        protected override void AttachToComponentRegistration(IComponentRegistryBuilder componentRegistryBuilder,
            IComponentRegistration registration)
        {
            // Attach to the registration's pipeline build.
            registration.PipelineBuilding += (sender, pipeline) =>
            {
                // Add our middleware to the pipeline.
                pipeline.Use(middleware);
            };
        }
    }

    public class TestLog4NetMiddleware : IResolveMiddleware
    {
        public PipelinePhase Phase => PipelinePhase.ParameterSelection;

        public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
        {
            // Add our parameters.
            context.ChangeParameters(context.Parameters.Union(
                new[]
                {
                    new ResolvedParameter(
                        (p, i) => p.ParameterType == typeof(ILog),
                        (p, i) => LogManager.GetLogger(p.Member.DeclaringType)
                    ),
                }));

            // Continue the resolve.
            next(context);

            // Has an instance been activated?
            if (context.NewInstanceActivated)
            {
                var instanceType = context.Instance.GetType();

                // Get all the injectable properties to set.
                // If you wanted to ensure the properties were only UNSET properties,
                // here's where you'd do it.
                var properties = instanceType
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);

                // Set the properties located.
                foreach (var propToSet in properties)
                {
                    propToSet.SetValue(context.Instance, LogManager.GetLogger(instanceType), null);
                }
            }
        }
    }


    [TestClass]
    public class UnitTestLog4Net
    {
        public ILog logger { get; set; }
        public TestContext TestContext { get; set; }

        // Create your builder.
        private static ContainerBuilder container = new ContainerBuilder();

        private static IContainer builder;
        protected ILifetimeScope scope;

        [TestMethod]
        public void TestMethodLog4Net()
        {
            container.RegisterType<TestLog4NetMiddleware>()
                .As<IResolveMiddleware>()
                .AsSelf()
                ;

            container.RegisterServiceMiddleware<Log4NetMiddleware>(new Log4NetMiddleware())
                //.AsImplementedInterfaces()
                ;
            container.RegisterAssemblyModules(Assembly.GetExecutingAssembly());

            builder = container.Build();

            scope = builder.BeginLifetimeScope();
            logger.Info("TEST");
        }
    }
}

However the test always fails with:

TestProject1.UnitTestLog4Net.TestMethodLog4Net threw exception: 
Autofac.Core.DependencyResolutionException: An exception was thrown
while activating λ:Autofac.Core.IModule[] ->
TestProject1.TestMiddlewareModule. --->
Autofac.Core.DependencyResolutionException: None of the constructors
found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'TestProject1.TestMiddlewareModule' can be invoked with the available
services and parameters: Cannot resolve parameter
'Autofac.Core.Resolving.Pipeline.IResolveMiddleware middleware' of
constructor 'Void
.ctor(Autofac.Core.Resolving.Pipeline.IResolveMiddleware)'. 

But the line container.RegisterType<TestLog4NetMiddleware>().As<IResolveMiddleware>().AsSelf(); should have registered TestLog4NetMiddleware as a valid IResolveMiddleware

What am I doing wrong?


Solution

  • Per the documentation on modules:

    Modules do not, themselves, go through dependency injection. They are used to configure the container, they are not actually registered and resolved like other components. If your module takes a constructor parameter, for example, you need to pass that in yourself. It won’t come from the container.

    Thus, to get the middleware in, you need to be a little more manual than using assembly scanning.

    1. Do not register the middleware in the container. Middleware, too, does not go through dependency injection.
    2. Do not register the middleware using module assembly scanning. As noted, that won't work.
    builder.RegisterModule(
      new MiddlewareModule(
        new Log4NetMiddleware()));