Make it easier to add hosting services
Fixes https://github.com/aspnet/Hosting/pull/146
This commit is contained in:
parent
34acb16e20
commit
2ee12735b3
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Hosting
|
|||
|
||||
public void Configure(IHostingEnvironment hostingEnv)
|
||||
{
|
||||
hostingEnv.EnvironmentName = _config.Get(EnvironmentKey) ?? hostingEnv.EnvironmentName;
|
||||
hostingEnv.EnvironmentName = _config?.Get(EnvironmentKey) ?? hostingEnv.EnvironmentName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,39 +14,61 @@ namespace Microsoft.AspNet.Hosting
|
|||
{
|
||||
public static class HostingServices
|
||||
{
|
||||
private static IServiceCollection Import(IServiceProvider fallbackProvider)
|
||||
private static IServiceCollection Import(IServiceProvider fallbackProvider, Action<IServiceCollection> configureHostServices)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
if (configureHostServices != null)
|
||||
{
|
||||
configureHostServices(services);
|
||||
}
|
||||
var manifest = fallbackProvider.GetRequiredService<IServiceManifest>();
|
||||
foreach (var service in manifest.Services)
|
||||
{
|
||||
services.AddTransient(service, sp => fallbackProvider.GetService(service));
|
||||
}
|
||||
services.AddSingleton<IServiceManifest>(sp => new HostingManifest(services));
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection Create(IConfiguration configuration = null)
|
||||
public static IServiceCollection Create()
|
||||
{
|
||||
return Create(CallContextServiceLocator.Locator.ServiceProvider, configuration);
|
||||
return Create(CallContextServiceLocator.Locator.ServiceProvider, configureHostServices: null, configuration: null);
|
||||
}
|
||||
|
||||
public static IServiceCollection Create(IServiceProvider fallbackServices, IConfiguration configuration = null)
|
||||
public static IServiceCollection Create(IServiceProvider fallbackServices)
|
||||
{
|
||||
configuration = configuration ?? new Configuration();
|
||||
var services = Import(fallbackServices);
|
||||
return Create(fallbackServices, configureHostServices: null, configuration: null);
|
||||
}
|
||||
|
||||
public static IServiceCollection Create(IServiceProvider fallbackServices, Action<IServiceCollection> configureHostServices)
|
||||
{
|
||||
return Create(fallbackServices, configureHostServices, configuration: null);
|
||||
}
|
||||
|
||||
public static IServiceCollection Create(Action<IServiceCollection> configureHostServices, IConfiguration configuration)
|
||||
{
|
||||
return Create(CallContextServiceLocator.Locator.ServiceProvider, configureHostServices, configuration);
|
||||
}
|
||||
|
||||
public static IServiceCollection Create(IServiceProvider fallbackServices, IConfiguration configuration)
|
||||
{
|
||||
return Create(CallContextServiceLocator.Locator.ServiceProvider, configureHostServices: null, configuration: configuration);
|
||||
}
|
||||
|
||||
public static IServiceCollection Create(IServiceProvider fallbackServices, Action<IServiceCollection> configureHostServices, IConfiguration configuration)
|
||||
{
|
||||
var services = Import(fallbackServices, configureHostServices);
|
||||
services.AddHosting(configuration);
|
||||
services.AddSingleton<IServiceManifest>(sp => new HostingManifest(fallbackServices));
|
||||
return services;
|
||||
}
|
||||
|
||||
// Manifest exposes the fallback manifest in addition to ITypeActivator, IHostingEnvironment, and ILoggerFactory
|
||||
private class HostingManifest : IServiceManifest
|
||||
{
|
||||
public HostingManifest(IServiceProvider fallback)
|
||||
public HostingManifest(IServiceCollection hostServices)
|
||||
{
|
||||
var manifest = fallback.GetRequiredService<IServiceManifest>();
|
||||
Services = new Type[] { typeof(ITypeActivator), typeof(IHostingEnvironment), typeof(ILoggerFactory), typeof(IHttpContextAccessor) }
|
||||
.Concat(manifest.Services).Distinct();
|
||||
.Concat(hostServices.Select(s => s.ServiceType)).Distinct();
|
||||
}
|
||||
|
||||
public IEnumerable<Type> Services { get; private set; }
|
||||
|
|
|
|||
|
|
@ -12,8 +12,13 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
{
|
||||
public static class HostingServicesExtensions
|
||||
{
|
||||
public static IServiceCollection AddLogging(this IServiceCollection services)
|
||||
{
|
||||
return services.AddLogging(config: null);
|
||||
}
|
||||
|
||||
// REVIEW: Logging doesn't depend on DI, where should this live?
|
||||
public static IServiceCollection AddLogging(this IServiceCollection services, IConfiguration config = null)
|
||||
public static IServiceCollection AddLogging(this IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
var describe = new ServiceDescriber(config);
|
||||
services.TryAdd(describe.Singleton<ILoggerFactory, LoggerFactory>());
|
||||
|
|
@ -21,7 +26,12 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHosting(this IServiceCollection services, IConfiguration configuration = null)
|
||||
public static IServiceCollection AddHosting(this IServiceCollection services)
|
||||
{
|
||||
return services.AddHosting(configuration: null);
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHosting(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var describer = new ServiceDescriber(configuration);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
// 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.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Hosting;
|
||||
|
|
@ -49,12 +46,22 @@ namespace Microsoft.AspNet.TestHost
|
|||
|
||||
public static TestServer Create(Action<IApplicationBuilder> app)
|
||||
{
|
||||
return Create(CallContextServiceLocator.Locator.ServiceProvider, app);
|
||||
return Create(CallContextServiceLocator.Locator.ServiceProvider, app, configureHostServices: null);
|
||||
}
|
||||
|
||||
public static TestServer Create(Action<IApplicationBuilder> app, Action<IServiceCollection> configureHostServices)
|
||||
{
|
||||
return Create(CallContextServiceLocator.Locator.ServiceProvider, app, configureHostServices);
|
||||
}
|
||||
|
||||
public static TestServer Create(IServiceProvider serviceProvider, Action<IApplicationBuilder> app)
|
||||
{
|
||||
var appServices = HostingServices.Create(serviceProvider).BuildServiceProvider();
|
||||
return Create(serviceProvider, app, configureHostServices: null);
|
||||
}
|
||||
|
||||
public static TestServer Create(IServiceProvider serviceProvider, Action<IApplicationBuilder> app, Action<IServiceCollection> configureHostServices)
|
||||
{
|
||||
var appServices = HostingServices.Create(serviceProvider, configureHostServices).BuildServiceProvider();
|
||||
var config = new Configuration();
|
||||
return new TestServer(config, appServices, app);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,73 @@ namespace Microsoft.AspNet.Hosting.Tests
|
|||
Assert.Null(provider.GetService<IFakeScopedService>()); // Make sure we don't leak non manifest services
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateCanAddAdditionalServices()
|
||||
{
|
||||
// Arrange
|
||||
var fallbackServices = new ServiceCollection();
|
||||
fallbackServices.AddTransient<IFakeService, FakeService>();
|
||||
fallbackServices.AddTransient<IFakeScopedService, FakeService>(); // Don't register in manifest
|
||||
|
||||
fallbackServices.AddInstance<IServiceManifest>(new ServiceManifest(
|
||||
new Type[] {
|
||||
typeof(IFakeService),
|
||||
}));
|
||||
|
||||
var instance = new FakeService();
|
||||
var factoryInstance = new FakeFactoryService(instance);
|
||||
|
||||
var services = HostingServices.Create(fallbackServices.BuildServiceProvider(),
|
||||
additionalHostServices =>
|
||||
{
|
||||
additionalHostServices.AddSingleton<IFakeSingletonService, FakeService>();
|
||||
additionalHostServices.AddInstance<IFakeServiceInstance>(instance);
|
||||
additionalHostServices.AddSingleton<IFactoryService>(serviceProvider => factoryInstance);
|
||||
});
|
||||
|
||||
// Act
|
||||
var provider = services.BuildServiceProvider();
|
||||
var singleton = provider.GetRequiredService<IFakeSingletonService>();
|
||||
var transient = provider.GetRequiredService<IFakeService>();
|
||||
var factory = provider.GetRequiredService<IFactoryService>();
|
||||
var manifest = provider.GetRequiredService<IServiceManifest>();
|
||||
|
||||
// Assert
|
||||
Assert.Same(singleton, provider.GetRequiredService<IFakeSingletonService>());
|
||||
Assert.NotSame(transient, provider.GetRequiredService<IFakeService>());
|
||||
Assert.Same(instance, provider.GetRequiredService<IFakeServiceInstance>());
|
||||
Assert.Same(factoryInstance, factory);
|
||||
Assert.Same(factory.FakeService, instance);
|
||||
Assert.Null(provider.GetService<INonexistentService>());
|
||||
Assert.Null(provider.GetService<IFakeScopedService>()); // Make sure we don't leak non manifest services
|
||||
Assert.Contains(typeof(IFakeSingletonService), manifest.Services);
|
||||
Assert.Contains(typeof(IFakeServiceInstance), manifest.Services);
|
||||
Assert.Contains(typeof(IFactoryService), manifest.Services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateAdditionalServicesDoNotOverrideFallback()
|
||||
{
|
||||
// Arrange
|
||||
var fallbackServices = new ServiceCollection();
|
||||
fallbackServices.AddTransient<IFakeService, FakeService>();
|
||||
|
||||
fallbackServices.AddInstance<IServiceManifest>(new ServiceManifest(
|
||||
new Type[] {
|
||||
typeof(IFakeService),
|
||||
}));
|
||||
|
||||
var services = HostingServices.Create(fallbackServices.BuildServiceProvider(),
|
||||
additionalHostServices => additionalHostServices.AddSingleton<IFakeService, FakeService>());
|
||||
|
||||
// Act
|
||||
var provider = services.BuildServiceProvider();
|
||||
var stillTransient = provider.GetRequiredService<IFakeService>();
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(stillTransient, provider.GetRequiredService<IFakeService>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanHideImportedServices()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ namespace Microsoft.AspNet.TestHost
|
|||
[Fact]
|
||||
public async Task CanAccessHttpContext()
|
||||
{
|
||||
var services = new ServiceCollection().BuildServiceProvider();
|
||||
TestServer server = TestServer.Create(app =>
|
||||
{
|
||||
app.Run(context =>
|
||||
|
|
@ -54,6 +53,35 @@ namespace Microsoft.AspNet.TestHost
|
|||
Assert.Equal("HasContext:True", result);
|
||||
}
|
||||
|
||||
public class ContextHolder
|
||||
{
|
||||
public ContextHolder(IHttpContextAccessor accessor)
|
||||
{
|
||||
Accessor = accessor;
|
||||
}
|
||||
|
||||
public IHttpContextAccessor Accessor { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanAddNewHostServices()
|
||||
{
|
||||
TestServer server = TestServer.Create(app =>
|
||||
{
|
||||
var a = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
|
||||
|
||||
app.Run(context =>
|
||||
{
|
||||
var b = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
|
||||
var accessor = app.ApplicationServices.GetRequiredService<ContextHolder>();
|
||||
return context.Response.WriteAsync("HasContext:" + (accessor.Accessor.Value != null));
|
||||
});
|
||||
}, newHostServices => newHostServices.AddSingleton<ContextHolder>());
|
||||
|
||||
string result = await server.CreateClient().GetStringAsync("/path");
|
||||
Assert.Equal("HasContext:True", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateInvokesApp()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue