Switch to IServiceProvidersFeature for RequestServices
This commit is contained in:
parent
285da613e4
commit
49520a2a73
|
|
@ -0,0 +1,70 @@
|
||||||
|
// 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.AspNet.Http.Features.Internal;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Hosting.Internal
|
||||||
|
{
|
||||||
|
public class RequestServicesFeature : IServiceProvidersFeature, IDisposable
|
||||||
|
{
|
||||||
|
private IServiceProvider _appServices;
|
||||||
|
private IServiceProvider _requestServices;
|
||||||
|
private IServiceScope _scope;
|
||||||
|
private bool _requestServicesSet;
|
||||||
|
|
||||||
|
public RequestServicesFeature(IServiceProvider applicationServices)
|
||||||
|
{
|
||||||
|
if (applicationServices == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(applicationServices));
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationServices = applicationServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IServiceProvider ApplicationServices
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _appServices;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
_appServices = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IServiceProvider RequestServices
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_requestServicesSet)
|
||||||
|
{
|
||||||
|
_scope = ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
||||||
|
_requestServices = _scope.ServiceProvider;
|
||||||
|
_requestServicesSet = true;
|
||||||
|
}
|
||||||
|
return _requestServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_requestServicesSet = true;
|
||||||
|
RequestServices = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_scope?.Dispose();
|
||||||
|
_scope = null;
|
||||||
|
_requestServices = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,8 @@ using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Features;
|
||||||
|
using Microsoft.AspNet.Http.Features.Internal;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Hosting.Internal
|
namespace Microsoft.AspNet.Hosting.Internal
|
||||||
|
|
@ -20,7 +22,6 @@ namespace Microsoft.AspNet.Hosting.Internal
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(next));
|
throw new ArgumentNullException(nameof(next));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (services == null)
|
if (services == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(services));
|
throw new ArgumentNullException(nameof(services));
|
||||||
|
|
@ -37,32 +38,26 @@ namespace Microsoft.AspNet.Hosting.Internal
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
// All done if there request services is set
|
var existingFeature = httpContext.Features.Get<IServiceProvidersFeature>();
|
||||||
if (httpContext.RequestServices != null)
|
|
||||||
|
// All done if request services is set
|
||||||
|
if (existingFeature?.RequestServices != null)
|
||||||
{
|
{
|
||||||
await _next.Invoke(httpContext);
|
await _next.Invoke(httpContext);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var priorApplicationServices = httpContext.ApplicationServices;
|
using (var feature = new RequestServicesFeature(_services))
|
||||||
var serviceProvider = priorApplicationServices ?? _services;
|
|
||||||
var scopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// Creates the scope and temporarily swap services
|
try
|
||||||
using (var scope = scopeFactory.CreateScope())
|
|
||||||
{
|
{
|
||||||
httpContext.ApplicationServices = serviceProvider;
|
httpContext.Features.Set<IServiceProvidersFeature>(feature);
|
||||||
httpContext.RequestServices = scope.ServiceProvider;
|
|
||||||
|
|
||||||
await _next.Invoke(httpContext);
|
await _next.Invoke(httpContext);
|
||||||
}
|
}
|
||||||
}
|
finally
|
||||||
finally
|
{
|
||||||
{
|
httpContext.Features.Set(existingFeature);
|
||||||
httpContext.RequestServices = null;
|
}
|
||||||
httpContext.ApplicationServices = priorApplicationServices;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Hosting;
|
using Microsoft.AspNet.Hosting;
|
||||||
using Microsoft.AspNet.Hosting.Startup;
|
using Microsoft.AspNet.Hosting.Startup;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Features;
|
||||||
|
using Microsoft.AspNet.Http.Features.Internal;
|
||||||
using Microsoft.Framework.Configuration;
|
using Microsoft.Framework.Configuration;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using Microsoft.Framework.Logging;
|
using Microsoft.Framework.Logging;
|
||||||
|
|
@ -133,6 +135,103 @@ namespace Microsoft.AspNet.TestHost
|
||||||
Assert.Equal("Found:True", result);
|
Assert.Equal("Found:True", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SettingApplicationServicesOnFeatureToNullThrows()
|
||||||
|
{
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
var feature = context.Features.Get<IServiceProvidersFeature>();
|
||||||
|
Assert.Throws<ArgumentNullException>(() => feature.ApplicationServices = null);
|
||||||
|
return context.Response.WriteAsync("Success");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
string result = await server.CreateClient().GetStringAsync("/path");
|
||||||
|
Assert.Equal("Success", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReplaceServiceProvidersFeatureFilter : IStartupFilter, IServiceProvidersFeature
|
||||||
|
{
|
||||||
|
public ReplaceServiceProvidersFeatureFilter(IServiceProvider appServices, IServiceProvider requestServices)
|
||||||
|
{
|
||||||
|
ApplicationServices = appServices;
|
||||||
|
RequestServices = requestServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IServiceProvider ApplicationServices { get; set; }
|
||||||
|
|
||||||
|
public IServiceProvider RequestServices { get; set; }
|
||||||
|
|
||||||
|
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
||||||
|
{
|
||||||
|
return app =>
|
||||||
|
{
|
||||||
|
app.Use(async (context, nxt) =>
|
||||||
|
{
|
||||||
|
context.Features.Set<IServiceProvidersFeature>(this);
|
||||||
|
await nxt();
|
||||||
|
});
|
||||||
|
next(app);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExistingServiceProviderFeatureWillNotBeReplaced()
|
||||||
|
{
|
||||||
|
var appServices = new ServiceCollection().BuildServiceProvider();
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
Assert.Equal(appServices, context.ApplicationServices);
|
||||||
|
Assert.Equal(appServices, context.RequestServices);
|
||||||
|
return context.Response.WriteAsync("Success");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
services => services.AddInstance<IStartupFilter>(new ReplaceServiceProvidersFeatureFilter(appServices, appServices)));
|
||||||
|
var result = await server.CreateClient().GetStringAsync("/path");
|
||||||
|
Assert.Equal("Success", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NullServiceProvidersFeatureFilter : IStartupFilter, IServiceProvidersFeature
|
||||||
|
{
|
||||||
|
public IServiceProvider ApplicationServices { get; set; }
|
||||||
|
|
||||||
|
public IServiceProvider RequestServices { get; set; }
|
||||||
|
|
||||||
|
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
||||||
|
{
|
||||||
|
return app =>
|
||||||
|
{
|
||||||
|
app.Use(async (context, nxt) =>
|
||||||
|
{
|
||||||
|
context.Features.Set<IServiceProvidersFeature>(this);
|
||||||
|
await nxt();
|
||||||
|
});
|
||||||
|
next(app);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WillReplaceServiceProviderFeatureWithNullRequestServices()
|
||||||
|
{
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
Assert.NotNull(context.ApplicationServices);
|
||||||
|
Assert.NotNull(context.RequestServices);
|
||||||
|
return context.Response.WriteAsync("Success");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
services => services.AddTransient<IStartupFilter, NullServiceProvidersFeatureFilter>());
|
||||||
|
var result = await server.CreateClient().GetStringAsync("/path");
|
||||||
|
Assert.Equal("Success", result);
|
||||||
|
}
|
||||||
|
|
||||||
public class EnsureApplicationServicesFilter : IStartupFilter
|
public class EnsureApplicationServicesFilter : IStartupFilter
|
||||||
{
|
{
|
||||||
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue