简体   繁体   中英

Registering an existing Autofac container with ASP.NET Core

I'm currently building a server application that speaks a custom protocol over TCP. This server application currently uses Autofac for dependency injection.

Recently though, I've added an ASP.NET Core project to the solution to provide an HTTP API on to op the server application, mostly to provide a management web application.

Autofac provides integration for ASP.NET Core projects, but I'm having difficulty figuring out how I can tell the ASP.NET Core service configuration to use the Autofac service container that I've already built, so that my TCP serve and HTTP API can resolve services from the same container.

Autofac provides documentation for this:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // Add services to the collection.
    services.AddMvc();

    // Create the container builder.
    var builder = new ContainerBuilder();

    builder.Populate(services);
    builder.RegisterType<MyType>().As<IMyType>();
    this.ApplicationContainer = builder.Build();

    // Create the IServiceProvider based on the container.
    return new AutofacServiceProvider(this.ApplicationContainer);
    //                                                   ^
    // This is where I'd like to insert my container ━━━━┛
}

The example only shows how to build a complete container from scratch for an ASP.NET Core application, but now how to re-use an existing container.

Using the ConfigureServices method on an IWebHostBuilder doesn't work here, because that has a return type of void , so I can't return an AutofacServiceProvider there.

Here's how I start up my application:

var containerBuilder = new ContainerBuilder();

// Here's where I register all my services
containerBuilder.RegisterModule(new ApplicationModule());
var container = containerBuilder.Build();

using (var scope = container.BeginLifetimeScope())
{

    var server = scope.Resolve<TcpServer>();

    var isService = args.Contains("--winservice");

    // Construct the HTTP API
    var builder = WebHost
        .CreateDefaultBuilder(args.Where(arg => arg != "--winservice").ToArray())
        .ConfigureServices(collection =>
        {
            // Not sure what to do here?
            // I can call `collection.AddAutofac` here, not sure what that does
        })
        .UseStartup<ApplicationAPI>();

    // Start the HTTP API
    if (isService)
    {
        // Run as a Win Service
        builder.Build().RunAsCustomService(server);
    }
    else
    {
        await server.Start();

        // Do not run as a Win Service
        builder.Build().Run();
    }
}

So how can I use the same container to resolve dependencies in both my TCP server and HTTP API?

Thanks!

I believe you should be able to replace the default (built-in) asp.net core container with your Autofac container. In that case, Startup.ConfigureServices must return IServiceProvider (rather than void).

Look at the "Default service container replacement" section in this documentation. The relevant section is towards the end of the page. You should be able to adapt this to your situation.

Hopefully this works for you. If it does, please mark this as the answer.

You could also try to provide an aggregated service provider, something like the one provided here ( https://github.com/kephas-software/kephas/blob/master/src/Kephas.Composition.DependencyInjection/Composition/DependencyInjection/AggregatedServiceProvider.cs ):

/// <summary>
/// An aggregated service provider.
/// </summary>
public class AggregatedServiceProvider : IServiceProvider, IDisposable
{
    private readonly IList<IServiceProvider> innerServiceProviders;

    /// <summary>
    /// Initializes a new instance of the <see cref="AggregatedServiceProvider"/> class.
    /// </summary>
    /// <param name="innerServiceProviders">A variable-length parameters list containing inner
    ///                                     service providers.</param>
    public AggregatedServiceProvider(params IServiceProvider[] innerServiceProviders)
    {
        this.innerServiceProviders = innerServiceProviders.ToList();
    }

    /// <summary>
    /// Gets the service object of the specified type.
    /// </summary>
    /// <param name="serviceType">An object that specifies the type of service object to get.</param>
    /// <returns>
    /// A service object of type <paramref name="serviceType">serviceType</paramref>.   -or-  null if
    /// there is no service object of type <paramref name="serviceType">serviceType</paramref>.
    /// </returns>
    public object GetService(Type serviceType)
    {
        foreach (var serviceProvider in this.innerServiceProviders)
        {
            var service = serviceProvider.GetService(serviceType);
            if (service != null)
            {
                return service;
            }
        }

        return null;
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged
    /// resources.
    /// </summary>
    public void Dispose()
    {
        foreach (var serviceProvider in this.innerServiceProviders)
        {
            (serviceProvider as IDisposable)?.Dispose();
        }

        this.innerServiceProviders.Clear();
    }
}

Then, change the code you have in configure services to:

return new AgregegatedServiceProvider(new ServiceProvider(services), myAutofacContainer);

The drawback is that the services from ASP.NET Core will not be available to your Autofac container, but, as I see, this should not be a problem for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM