Add service provider factory support
Fixes: #18814 This adds back support on the Blazor WASM Host for using ISerivceProviderFactory<>. We previously had this support when the Blazor WASM host was a clone of generic host, but I accidentally lost it when simplifying the host (sorry :( ).
This commit is contained in:
parent
e8d2d562aa
commit
9c16db3e89
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Services;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
|
@ -20,6 +19,8 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
/// </summary>
|
||||
public sealed class WebAssemblyHostBuilder
|
||||
{
|
||||
private Func<IServiceProvider> _createServiceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="WebAssemblyHostBuilder"/> using the most common
|
||||
/// conventions and settings.
|
||||
|
|
@ -53,6 +54,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
Services = new ServiceCollection();
|
||||
|
||||
InitializeDefaultServices();
|
||||
|
||||
_createServiceProvider = () =>
|
||||
{
|
||||
return Services.BuildServiceProvider();
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -71,6 +77,40 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
/// </summary>
|
||||
public IServiceCollection Services { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="IServiceProviderFactory{TBuilder}" /> instance to be used to create the <see cref="IServiceProvider" />.
|
||||
/// </summary>
|
||||
/// <param name="factory">The <see cref="IServiceProviderFactory{TBuilder}" />.</param>
|
||||
/// <param name="configure">
|
||||
/// A delegate used to configure the <typeparamref T="TBuilder" />. This can be used to configure services using
|
||||
/// APIS specific to the <see cref="IServiceProviderFactory{TBuilder}" /> implementation.
|
||||
/// </param>
|
||||
/// <typeparam name="TBuilder">The type of builder provided by the <see cref="IServiceProviderFactory{TBuilder}" />.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <see cref="ConfigureContainer{TBuilder}(IServiceProviderFactory{TBuilder}, Action{TBuilder})"/> is called by <see cref="Build"/>
|
||||
/// and so the delegate provided by <paramref name="configure"/> will run after all other services have been registered.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Multiple calls to <see cref="ConfigureContainer{TBuilder}(IServiceProviderFactory{TBuilder}, Action{TBuilder})"/> will replace
|
||||
/// the previously stored <paramref name="factory"/> and <paramref name="configure"/> delegate.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void ConfigureContainer<TBuilder>(IServiceProviderFactory<TBuilder> factory, Action<TBuilder> configure = null)
|
||||
{
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
_createServiceProvider = () =>
|
||||
{
|
||||
var container = factory.CreateBuilder(Services);
|
||||
configure?.Invoke(container);
|
||||
return factory.CreateServiceProvider(container);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a <see cref="WebAssemblyHost"/> instance based on the configuration of this builder.
|
||||
/// </summary>
|
||||
|
|
@ -84,7 +124,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
// A Blazor application always runs in a scope. Since we want to make it possible for the user
|
||||
// to configure services inside *that scope* inside their startup code, we create *both* the
|
||||
// service provider and the scope here.
|
||||
var services = Services.BuildServiceProvider();
|
||||
var services = _createServiceProvider();
|
||||
var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
||||
|
||||
return new WebAssemblyHost(services, scope, configuration, RootComponents.ToArray());
|
||||
|
|
|
|||
|
|
@ -51,6 +51,76 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
|||
Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_AllowsConfiguringContainer()
|
||||
{
|
||||
// Arrange
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault();
|
||||
|
||||
builder.Services.AddScoped<StringBuilder>();
|
||||
var factory = new MyFakeServiceProviderFactory();
|
||||
builder.ConfigureContainer(factory);
|
||||
|
||||
// Act
|
||||
var host = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.True(factory.CreateServiceProviderCalled);
|
||||
Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_AllowsConfiguringContainer_WithDelegate()
|
||||
{
|
||||
// Arrange
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault();
|
||||
|
||||
builder.Services.AddScoped<StringBuilder>();
|
||||
|
||||
var factory = new MyFakeServiceProviderFactory();
|
||||
builder.ConfigureContainer(factory, builder =>
|
||||
{
|
||||
builder.ServiceCollection.AddScoped<List<string>>();
|
||||
});
|
||||
|
||||
// Act
|
||||
var host = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.True(factory.CreateServiceProviderCalled);
|
||||
Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
|
||||
Assert.NotNull(host.Services.GetRequiredService<List<string>>());
|
||||
}
|
||||
|
||||
private class MyFakeDIBuilderThing
|
||||
{
|
||||
public MyFakeDIBuilderThing(IServiceCollection serviceCollection)
|
||||
{
|
||||
ServiceCollection = serviceCollection;
|
||||
}
|
||||
|
||||
public IServiceCollection ServiceCollection { get; }
|
||||
}
|
||||
|
||||
private class MyFakeServiceProviderFactory : IServiceProviderFactory<MyFakeDIBuilderThing>
|
||||
{
|
||||
public bool CreateServiceProviderCalled { get; set; }
|
||||
|
||||
public MyFakeDIBuilderThing CreateBuilder(IServiceCollection services)
|
||||
{
|
||||
return new MyFakeDIBuilderThing(services);
|
||||
}
|
||||
|
||||
public IServiceProvider CreateServiceProvider(MyFakeDIBuilderThing containerBuilder)
|
||||
{
|
||||
// This is the best way to test the factory was actually used. The Host doesn't
|
||||
// expose the *root* service provider, only a scoped instance. So we can return
|
||||
// a different type here, but we have no way to inspect it.
|
||||
CreateServiceProviderCalled = true;
|
||||
return containerBuilder.ServiceCollection.BuildServiceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_AddsConfigurationToServices()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue