asp.net-web-api2owinautofacself-hostingtopshelf

Do I need to register autofac's container with itself to wire up webapi's depdendency resolver?


I am writing a Windows service using Topshelf that should start a self hosted webapi project and a FIX service based on quickfix/n. Please consider the shortened code below, which works so far.

However there is one problem - there are now two container instances living in my application. My guts tell me this is a bad idea, especially because I am loading MyBigModule two times. Also because one of my controllers require the same component than the one using quickfix.

// Assembly A referencing B
public class Program
{
    public static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        buider.RegisterModule<MyBigModule>();
        var container = builder.Build();

        _ = HostFactory.Run(c => 
        {
            c.UseAutofacContainer(container);
            c.Service<IMyServiceManager>(svc => 
            {
                svc.ConstructUsingAutofacContainer();
                // ...
            }

            // ...
        });
    }
}

// Assembly B
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = new HttpConfiguration();

        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly())              
        var container = builder.Build();

        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        // ...

        app.UseAutofacMiddleware(container);
        app.UseAutofacWebApi(config);
        app.UseWebApi(config);
    }
}

// Assembly B
public class WebHost : IWebHost
{
    // ...

    public void Start()
    {
        WebApp.Start<Startup>("someUrl");
    }
}

// Assembly B
public class MyBigModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.Register<WebHost>.As<IWebHost>();

        // ...
    }
}

My first approach was to pass an Action<IAppBuilder> to the WebHost constructor, that is created within Main(). Something like this:

public class Program
{
    public static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<MyBigModule>();
        var container = builder.Build();

        var webhost = new WebHost("someUrl", app => 
        {
            var config = new HttpConfiguration();            
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            // ....
        });

        builder.RegisterInstance(webost); 

        // ... 
    }
}

However I would have to build my container first and then add another registration later. Which doesn't follow the recommendation that a container should be considered immutable. Another alternativ would be to pass the container instance down to my WebHosts Startup class.

It seems that I need to have a registration of my container inside the container itself. How would I do that? Maybe there is a better approach? I hope it's clear what I am struggling with.

I am pretty sure there must be a better way to wire up webapi's resolver. Any ideas and feedback is very appreciated.


Solution

  • I solved it in the meantime, thanks to this post. We can inject an instance of ILifetimeScope to the constructor without having to register anything.

    // Assembly A referencing B
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            buider.RegisterModule<MyBigModule>();
            var container = builder.Build();
    
            _ = HostFactory.Run(c => 
            {
                c.UseAutofacContainer(container);
                c.Service<IMyServiceManager>(svc => 
                {
                    svc.ConstructUsingAutofacContainer();
                    // ...
                }
    
                // ...
            });
        }
    }
    
    // Assembly B
    public class WebHost : IWebHost
    {
        private readoly ILifetimeScope scope
    
        public WebHost(ILifetimeScope scope)
        {
            this.scope = scope;
        }
    
        public void Start()
        {
            WebApp.Start("someUri", app => {
                var config = new HttpConfiguration
                {
                    DependencyResolver = new AutofacWebApiDependencyResolver(this.scope)
                };
    
                // ...
            });        
        }
    }
    
    // Assembly B
    public class MyBigModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.Register<WebHost>.As<IWebHost>();
    
            // ...
        }
    }