Support IServiceProviderFactory in WebAssemblyHostBuilder (imported from Blazor PR 1623) (#4785)
This commit is contained in:
parent
54c1122582
commit
d30c407dd0
|
|
@ -17,6 +17,18 @@ namespace Microsoft.AspNetCore.Components.Hosting
|
|||
/// </summary>
|
||||
IDictionary<object, object> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the factory used to create the service provider.
|
||||
/// </summary>
|
||||
/// <returns>The same instance of the <see cref="IWebAssemblyHostBuilder"/> for chaining.</returns>
|
||||
IWebAssemblyHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory);
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the factory used to create the service provider.
|
||||
/// </summary>
|
||||
/// <returns>The same instance of the <see cref="IWebAssemblyHostBuilder"/> for chaining.</returns>
|
||||
IWebAssemblyHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<WebAssemblyHostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory);
|
||||
|
||||
/// <summary>
|
||||
/// Adds services to the container. This can be called multiple times and the results will be additive.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Hosting
|
||||
{
|
||||
// Equivalent to https://github.com/aspnet/Extensions/blob/master/src/Hosting/Hosting/src/Internal/IServiceFactoryAdapter.cs
|
||||
|
||||
internal interface IWebAssemblyServiceFactoryAdapter
|
||||
{
|
||||
object CreateBuilder(IServiceCollection services);
|
||||
|
||||
IServiceProvider CreateServiceProvider(object containerBuilder);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Components.Hosting
|
|||
private List<Action<WebAssemblyHostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<WebAssemblyHostBuilderContext, IServiceCollection>>();
|
||||
private bool _hostBuilt;
|
||||
private WebAssemblyHostBuilderContext _BrowserHostBuilderContext;
|
||||
private IWebAssemblyServiceFactoryAdapter _serviceProviderFactory = new WebAssemblyServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
|
||||
private IServiceProvider _appServices;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -39,6 +40,26 @@ namespace Microsoft.AspNetCore.Components.Hosting
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the factory used to create the service provider.
|
||||
/// </summary>
|
||||
/// <returns>The same instance of the <see cref="IWebAssemblyHostBuilder"/> for chaining.</returns>
|
||||
public IWebAssemblyHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
|
||||
{
|
||||
_serviceProviderFactory = new WebAssemblyServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the factory used to create the service provider.
|
||||
/// </summary>
|
||||
/// <returns>The same instance of the <see cref="IWebAssemblyHostBuilder"/> for chaining.</returns>
|
||||
public IWebAssemblyHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<WebAssemblyHostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
|
||||
{
|
||||
_serviceProviderFactory = new WebAssemblyServiceFactoryAdapter<TContainerBuilder>(() => _BrowserHostBuilderContext, factory ?? throw new ArgumentNullException(nameof(factory)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run the given actions to initialize the host. This can only be called once.
|
||||
/// </summary>
|
||||
|
|
@ -85,7 +106,8 @@ namespace Microsoft.AspNetCore.Components.Hosting
|
|||
configureServicesAction(_BrowserHostBuilderContext, services);
|
||||
}
|
||||
|
||||
_appServices = services.BuildServiceProvider();
|
||||
var builder = _serviceProviderFactory.CreateBuilder(services);
|
||||
_appServices = _serviceProviderFactory.CreateServiceProvider(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Hosting
|
||||
{
|
||||
// Equivalent to https://github.com/aspnet/Extensions/blob/master/src/Hosting/Hosting/src/Internal/ServiceFactoryAdapter.cs
|
||||
|
||||
internal class WebAssemblyServiceFactoryAdapter<TContainerBuilder> : IWebAssemblyServiceFactoryAdapter
|
||||
{
|
||||
private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory;
|
||||
private readonly Func<WebAssemblyHostBuilderContext> _contextResolver;
|
||||
private Func<WebAssemblyHostBuilderContext, IServiceProviderFactory<TContainerBuilder>> _factoryResolver;
|
||||
|
||||
public WebAssemblyServiceFactoryAdapter(IServiceProviderFactory<TContainerBuilder> serviceProviderFactory)
|
||||
{
|
||||
_serviceProviderFactory = serviceProviderFactory ?? throw new ArgumentNullException(nameof(serviceProviderFactory));
|
||||
}
|
||||
|
||||
public WebAssemblyServiceFactoryAdapter(Func<WebAssemblyHostBuilderContext> contextResolver, Func<WebAssemblyHostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factoryResolver)
|
||||
{
|
||||
_contextResolver = contextResolver ?? throw new ArgumentNullException(nameof(contextResolver));
|
||||
_factoryResolver = factoryResolver ?? throw new ArgumentNullException(nameof(factoryResolver));
|
||||
}
|
||||
|
||||
public object CreateBuilder(IServiceCollection services)
|
||||
{
|
||||
if (_serviceProviderFactory == null)
|
||||
{
|
||||
_serviceProviderFactory = _factoryResolver(_contextResolver());
|
||||
|
||||
if (_serviceProviderFactory == null)
|
||||
{
|
||||
throw new InvalidOperationException("The resolver returned a null IServiceProviderFactory");
|
||||
}
|
||||
}
|
||||
return _serviceProviderFactory.CreateBuilder(services);
|
||||
}
|
||||
|
||||
public IServiceProvider CreateServiceProvider(object containerBuilder)
|
||||
{
|
||||
if (_serviceProviderFactory == null)
|
||||
{
|
||||
throw new InvalidOperationException("CreateBuilder must be called before CreateServiceProvider");
|
||||
}
|
||||
|
||||
return _serviceProviderFactory.CreateServiceProvider((TContainerBuilder)containerBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.JSInterop;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Hosting
|
||||
|
|
@ -76,5 +78,86 @@ namespace Microsoft.AspNetCore.Components.Hosting
|
|||
services.AddSingleton<string>("foo");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HostBuilder_CanCustomizeServiceFactory()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new WebAssemblyHostBuilder();
|
||||
builder.UseServiceProviderFactory(new TestServiceProviderFactory());
|
||||
|
||||
// Act
|
||||
var host = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.IsType<TestServiceProvider>(host.Services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HostBuilder_CanCustomizeServiceFactoryWithContext()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new WebAssemblyHostBuilder();
|
||||
builder.UseServiceProviderFactory(context =>
|
||||
{
|
||||
Assert.NotNull(context.Properties);
|
||||
Assert.Same(builder.Properties, context.Properties);
|
||||
return new TestServiceProviderFactory();
|
||||
});
|
||||
|
||||
// Act
|
||||
var host = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.IsType<TestServiceProvider>(host.Services);
|
||||
}
|
||||
|
||||
private class TestServiceProvider : IServiceProvider
|
||||
{
|
||||
private readonly IServiceProvider _underlyingProvider;
|
||||
|
||||
public TestServiceProvider(IServiceProvider underlyingProvider)
|
||||
{
|
||||
_underlyingProvider = underlyingProvider;
|
||||
}
|
||||
|
||||
public object GetService(Type serviceType)
|
||||
{
|
||||
if (serviceType == typeof(IWebAssemblyHost))
|
||||
{
|
||||
// Since the test will make assertions about the resulting IWebAssemblyHost,
|
||||
// show that custom DI containers have the power to substitute themselves
|
||||
// as the IServiceProvider
|
||||
return new WebAssemblyHost(
|
||||
this, _underlyingProvider.GetRequiredService<IJSRuntime>());
|
||||
}
|
||||
else
|
||||
{
|
||||
return _underlyingProvider.GetService(serviceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestServiceProviderFactory : IServiceProviderFactory<IServiceCollection>
|
||||
{
|
||||
public IServiceCollection CreateBuilder(IServiceCollection services)
|
||||
{
|
||||
return new TestServiceCollection(services);
|
||||
}
|
||||
|
||||
public IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
|
||||
{
|
||||
Assert.IsType<TestServiceCollection>(serviceCollection);
|
||||
return new TestServiceProvider(serviceCollection.BuildServiceProvider());
|
||||
}
|
||||
|
||||
class TestServiceCollection : List<ServiceDescriptor>, IServiceCollection
|
||||
{
|
||||
public TestServiceCollection(IEnumerable<ServiceDescriptor> collection)
|
||||
: base(collection)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue