Search code examples
c#.netautofacnancy

Autofac Property Injection in Nancy Module


I am using Autofac for DI and i have NacyModule like:

public class TestModule: NancyModule
{
    public ISessionFactory SessionFactory { get; set; }
    public IMapper Mapper { get; set; }

    public TestModule(ITestRepository testRepository)
    {
        Get("hello", _ => "hello world");
    }
}

My AutoFac configuration

In Startup.cs

    var builder = new ContainerBuilder();

                builder.RegisterModule(new ServicesModule());
                builder.RegisterModule(new NHibernateModule(configuration.GetConnectionString("DefaultConnection")));
                builder.RegisterModule(new AutomapperModule());
                builder.Populate(services);
                container = builder.Build();

                return new AutofacServiceProvider(container);

in ServiceModule.cs 

    builder.RegisterAssemblyTypes(ThisAssembly)
                               .Where(t => new[]
                        {
                           "Processor",
                            "Process",
                            "Checker",
                            "Indexer",
                            "Searcher",
                            "Translator",
                            "Mapper",
                            "Exporter",
                            "Repository"         }.Any(y =>
                        {
                            var a = t.Name;
                            return a.EndsWith(y);
                        }))
                    .AsSelf()
                    .AsImplementedInterfaces()
                    .PropertiesAutowired()
                    .InstancePerLifetimeScope();

in NHibernateModule.cs

    builder.Register(c => CreateConfiguration(connectionString)).SingleInstance();
    builder.Register(c => c.Resolve<Configuration>().BuildSessionFactory()).As<ISessionFactory>().SingleInstance().PropertiesAutowired();

And in my nancy bootstraper I have something like this

 public class Bootstrapper : AutofacNancyBootstrapper
    {
        private static readonly ILogger logger = LogManager.GetLogger(typeof(Bootstrapper).FullName);

        private readonly ILifetimeScope _container;

        public Bootstrapper(ILifetimeScope container)
        {

            _container = container;
        }

        protected override ILifetimeScope GetApplicationContainer()
        {
            return _container;
        }

        public override void Configure(INancyEnvironment environment)
        {
            base.Configure(environment);

            environment.Tracing(false, true);
        }

        protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
        {
            container.Update(builder =>
            {
                builder.Register(c =>
                {
                    var sf = c.Resolve<ISessionFactory>();
                    return new Lazy<NHibernate.ISession>(() =>
                    {
                        var s = sf.OpenSession();
                        s.BeginTransaction();
                        return s;
                    });
                }).InstancePerLifetimeScope();

                builder.Register(c => c.Resolve<Lazy<NHibernate.ISession>>().Value).As<NHibernate.ISession>();
            });
        }
}

I now about constructor injection, works ok, and property injection works ok in other classes, but not works in nancy modules

Note I tried adding .PropertiesAutowired() in ConfigureRequestContainer after the container update

thanks.


Solution

  • The AutofacNancyBootstrapper class automatically register the module in Autofac even if the service is already registered :

    AutofacNancyBootstrapper.cs

    protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
    {
        return container.Update(builder => builder.RegisterType(moduleType)
                                                  .As<INancyModule>())
                        .Resolve<INancyModule>();
    }
    

    With the default implementation the module is always registered and PropertiesAutoWired is not applied.

    To change this, you can override the method like this :

    protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
    {
        return container.Update(builder => builder.RegisterType(moduleType)
                                                  .As<INancyModule>())
                        .Resolve<INancyModule>()
                        .PropertiesAutoWired();
    }
    

    Or change it like this :

    protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
    {
        INancyModule module = null;
    
        if (container.IsRegistered(moduleType))
        {
            module = container.Resolve(moduleType) as INancyModule;
        }
        else
        {
            IEnumerable<IComponentRegistration> registrations = container.ComponentRegistry.RegistrationsFor(new TypedService(typeof(INancyModule)));
            IComponentRegistration registration = registrations.FirstOrDefault(r => r.Activator.LimitType == moduleType);
            if (registration != null)
            {
                module = container.ResolveComponent(registration, Enumerable.Empty<Parameter>()) as INancyModule;
            }
            else
            {
                module = base.GetModule(container, moduleType);
            }
        }
    
        return module;
    }
    

    and then register the module in your composition root

    builder.RegisterType<TestModule>()
           .As<INancyModule>()
           .PropertiesAutoWired()