Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Allow overriding the hosting service provider #1325

Merged
merged 9 commits into from
Feb 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/Microsoft.AspNetCore.Hosting/Internal/StartupLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,8 @@ IServiceProvider ConfigureServicesWithContainerConfiguration(IServiceCollection
{
// Get the default factory
var serviceProviderFactory = HostingServiceProvider.GetRequiredService<IServiceProviderFactory<IServiceCollection>>();

// Don't bother calling CreateBuilder since it just returns the default service collection
applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(services);
var builder = serviceProviderFactory.CreateBuilder(services);
Copy link
Member

@ENikS ENikS Jan 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it guaranteed that factory exists? What if serviceProviderFactory is null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been the case since 2.0. There’s always one registered. Unless somebody removes it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shrug it's a weird use of embedded methods.

applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(builder);
}

return applicationServiceProvider ?? services.BuildServiceProvider();
Expand Down
18 changes: 17 additions & 1 deletion src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public IWebHost Build()

var hostingServices = BuildCommonServices(out var hostingStartupErrors);
var applicationServices = hostingServices.Clone();
var hostingServiceProvider = hostingServices.BuildServiceProvider();
var hostingServiceProvider = GetProviderFromFactory(hostingServices);

if (!_options.SuppressStatusMessages)
{
Expand Down Expand Up @@ -202,6 +202,22 @@ public IWebHost Build()
host.Dispose();
throw;
}

IServiceProvider GetProviderFromFactory(IServiceCollection collection)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only used once and not complex, inline.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should remain a method. It's not like it's 2 lines of code. It's a few.

{
var provider = collection.BuildServiceProvider();
var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();

if (factory != null)
{
using (provider)
{
return factory.CreateServiceProvider(factory.CreateBuilder(collection));
}
}

return provider;
}
}

private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
Expand Down
84 changes: 84 additions & 0 deletions test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,45 @@ public void Build_RunsHostingStartupAssembliesBeforeApplication()
}
}


[Fact]
public async Task ExternalContainerInstanceCanBeUsedForEverything()
{
var disposables = new List<DisposableService>();

var containerFactory = new ExternalContainerFactory(services =>
{
services.AddSingleton(sp =>
{
var service = new DisposableService();
disposables.Add(service);
return service;
});
});

var host = new WebHostBuilder()
.UseStartup<StartupWithExternalServices>()
.UseServer(new TestServer())
.ConfigureServices(services =>
{
services.AddSingleton<IServiceProviderFactory<IServiceCollection>>(containerFactory);
})
.Build();

using (host)
{
await host.StartAsync();
}

// We should create the hosting service provider and the application service provider
Assert.Equal(2, containerFactory.ServiceProviders.Count);
Assert.Equal(2, disposables.Count);

Assert.NotEqual(disposables[0], disposables[1]);
Assert.True(disposables[0].Disposed);
Assert.True(disposables[1].Disposed);
}

[Fact]
public void Build_HostingStartupAssemblyCanBeExcluded()
{
Expand Down Expand Up @@ -1048,6 +1087,51 @@ public Task StartAsync<TContext>(IHttpApplication<TContext> application, Cancell
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

internal class ExternalContainerFactory : IServiceProviderFactory<IServiceCollection>
{
private readonly Action<IServiceCollection> _configureServices;
private readonly List<IServiceProvider> _serviceProviders = new List<IServiceProvider>();

public List<IServiceProvider> ServiceProviders => _serviceProviders;

public ExternalContainerFactory(Action<IServiceCollection> configureServices)
{
_configureServices = configureServices;
}

public IServiceCollection CreateBuilder(IServiceCollection services)
{
_configureServices(services);
return services;
}

public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
{
var provider = containerBuilder.BuildServiceProvider();
_serviceProviders.Add(provider);
return provider;
}
}

internal class StartupWithExternalServices
{
public DisposableService DisposableServiceCtor { get; set; }

public DisposableService DisposableServiceApp { get; set; }

public StartupWithExternalServices(DisposableService disposable)
{
DisposableServiceCtor = disposable;
}

public void ConfigureServices(IServiceCollection services) { }

public void Configure(IApplicationBuilder app, DisposableService disposable)
{
DisposableServiceApp = disposable;
}
}

internal class StartupVerifyServiceA : IStartup
{
internal ServiceA ServiceA { get; set; }
Expand Down