diff --git a/Hosting.sln b/Hosting.sln index def8ba2b14..76c17f7d3e 100644 --- a/Hosting.sln +++ b/Hosting.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30203.2 +VisualStudioVersion = 12.0.21005.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E0497F39-AFFB-4819-A116-E39E361915AB}" EndProject @@ -15,9 +15,17 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Hosting.Te EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{C30C98CD-3D69-4AE9-B680-0E0E6D8834C6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KWebStartup.net45", "samples\KWebStartup\KWebStartup.net45.csproj", "{E9B21845-E51A-4B8C-AF0B-0C4CE16550BF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.RequestContainer.net45", "src\Microsoft.AspNet.RequestContainer\Microsoft.AspNet.RequestContainer.net45.csproj", "{F4C7B46C-B39F-4172-9C3A-05352183D469}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KWebStartup.k10", "samples\KWebStartup\KWebStartup.k10.csproj", "{348007AA-AB91-48B2-98DB-57D068E2C1AE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.RequestContainer.k10", "src\Microsoft.AspNet.RequestContainer\Microsoft.AspNet.RequestContainer.k10.csproj", "{FD9833B9-3FB1-4612-BBB4-64F539DB0F8B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net45", "net45", "{93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k10", "k10", "{EB784E77-FD42-46EC-9C8C-502B78962407}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KWebStartup.k10", "samples\KWebStartup\KWebStartup.k10.csproj", "{C0235BA1-9198-42C0-92D8-7578E9B9D96B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KWebStartup.net45", "samples\KWebStartup\KWebStartup.net45.csproj", "{B5939234-73ED-4DAD-A2C6-D61AA6DFB406}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -37,23 +45,35 @@ Global {80588AF3-6B14-4D11-9DC4-1EF6453B54C9}.Debug|Any CPU.Build.0 = Debug|Any CPU {80588AF3-6B14-4D11-9DC4-1EF6453B54C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {80588AF3-6B14-4D11-9DC4-1EF6453B54C9}.Release|Any CPU.Build.0 = Release|Any CPU - {E9B21845-E51A-4B8C-AF0B-0C4CE16550BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E9B21845-E51A-4B8C-AF0B-0C4CE16550BF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E9B21845-E51A-4B8C-AF0B-0C4CE16550BF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E9B21845-E51A-4B8C-AF0B-0C4CE16550BF}.Release|Any CPU.Build.0 = Release|Any CPU - {348007AA-AB91-48B2-98DB-57D068E2C1AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {348007AA-AB91-48B2-98DB-57D068E2C1AE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {348007AA-AB91-48B2-98DB-57D068E2C1AE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {348007AA-AB91-48B2-98DB-57D068E2C1AE}.Release|Any CPU.Build.0 = Release|Any CPU + {F4C7B46C-B39F-4172-9C3A-05352183D469}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4C7B46C-B39F-4172-9C3A-05352183D469}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4C7B46C-B39F-4172-9C3A-05352183D469}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4C7B46C-B39F-4172-9C3A-05352183D469}.Release|Any CPU.Build.0 = Release|Any CPU + {FD9833B9-3FB1-4612-BBB4-64F539DB0F8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD9833B9-3FB1-4612-BBB4-64F539DB0F8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD9833B9-3FB1-4612-BBB4-64F539DB0F8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD9833B9-3FB1-4612-BBB4-64F539DB0F8B}.Release|Any CPU.Build.0 = Release|Any CPU + {C0235BA1-9198-42C0-92D8-7578E9B9D96B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0235BA1-9198-42C0-92D8-7578E9B9D96B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0235BA1-9198-42C0-92D8-7578E9B9D96B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0235BA1-9198-42C0-92D8-7578E9B9D96B}.Release|Any CPU.Build.0 = Release|Any CPU + {B5939234-73ED-4DAD-A2C6-D61AA6DFB406}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5939234-73ED-4DAD-A2C6-D61AA6DFB406}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5939234-73ED-4DAD-A2C6-D61AA6DFB406}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5939234-73ED-4DAD-A2C6-D61AA6DFB406}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D546290B-E280-4D99-BA9C-0D364A0AFB54} = {E0497F39-AFFB-4819-A116-E39E361915AB} - {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91} = {E0497F39-AFFB-4819-A116-E39E361915AB} + {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} = {E0497F39-AFFB-4819-A116-E39E361915AB} + {EB784E77-FD42-46EC-9C8C-502B78962407} = {E0497F39-AFFB-4819-A116-E39E361915AB} {80588AF3-6B14-4D11-9DC4-1EF6453B54C9} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0} - {E9B21845-E51A-4B8C-AF0B-0C4CE16550BF} = {C30C98CD-3D69-4AE9-B680-0E0E6D8834C6} - {348007AA-AB91-48B2-98DB-57D068E2C1AE} = {C30C98CD-3D69-4AE9-B680-0E0E6D8834C6} + {F4C7B46C-B39F-4172-9C3A-05352183D469} = {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} + {D546290B-E280-4D99-BA9C-0D364A0AFB54} = {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} + {FD9833B9-3FB1-4612-BBB4-64F539DB0F8B} = {EB784E77-FD42-46EC-9C8C-502B78962407} + {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91} = {EB784E77-FD42-46EC-9C8C-502B78962407} + {C0235BA1-9198-42C0-92D8-7578E9B9D96B} = {C30C98CD-3D69-4AE9-B680-0E0E6D8834C6} + {B5939234-73ED-4DAD-A2C6-D61AA6DFB406} = {C30C98CD-3D69-4AE9-B680-0E0E6D8834C6} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.RequestContainer/ContainerExtensions.cs b/src/Microsoft.AspNet.RequestContainer/ContainerExtensions.cs new file mode 100644 index 0000000000..4cfdb4210f --- /dev/null +++ b/src/Microsoft.AspNet.RequestContainer/ContainerExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.DependencyInjection.Fallback; + +namespace Microsoft.AspNet.RequestContainer +{ + public static class ContainerExtensions + { + public static IBuilder UseMiddleware(this IBuilder builder, Type middleware, params object[] args) + { + // TODO: move this ext method someplace nice + return builder.Use(next => + { + //TODO: this should be MethodInfo.CreateDelegate for coreclr + var typeActivator = builder.ServiceProvider.GetService(); + var instance = typeActivator.CreateInstance(middleware, new[] { next }.Concat(args).ToArray()); + return (RequestDelegate)Delegate.CreateDelegate(typeof(RequestDelegate), instance, "Invoke"); + }); + } + + public static IBuilder UseContainer(this IBuilder app) + { + return app.UseMiddleware(typeof(ContainerMiddleware)); + } + + public static IBuilder UseContainer(this IBuilder app, IServiceProvider applicationServices) + { + app.ServiceProvider = applicationServices; + + return app.UseMiddleware(typeof(ContainerMiddleware)); + } + + public static IBuilder UseContainer(this IBuilder app, IEnumerable applicationServices) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.Add(applicationServices); + app.ServiceProvider = serviceCollection.BuildServiceProvider(app.ServiceProvider); + + return app.UseMiddleware(typeof(ContainerMiddleware)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.RequestContainer/ContainerMiddleware.cs b/src/Microsoft.AspNet.RequestContainer/ContainerMiddleware.cs new file mode 100644 index 0000000000..d2295e90c1 --- /dev/null +++ b/src/Microsoft.AspNet.RequestContainer/ContainerMiddleware.cs @@ -0,0 +1,114 @@ +using System; +#if NET45 +using System.Runtime.Remoting.Messaging; +#endif +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; + +namespace Microsoft.AspNet.RequestContainer +{ + public class ContainerMiddleware + { + private const string LogicalDataKey = "__HttpContext_Current__"; + private readonly RequestDelegate _next; + private readonly IServiceProvider _rootServiceProvider; + private readonly IContextAccessor _rootHttpContextAccessor; + private readonly IServiceScopeFactory _rootServiceScopeFactory; + + public ContainerMiddleware( + RequestDelegate next, + IServiceProvider rootServiceProvider, + IContextAccessor rootHttpContextAccessor, + IServiceScopeFactory rootServiceScopeFactory) + { + if (rootServiceProvider == null) + { + throw new ArgumentNullException("rootServiceProvider"); + } + if (rootHttpContextAccessor == null) + { + throw new ArgumentNullException("rootHttpContextAccessor"); + } + if (rootServiceScopeFactory == null) + { + throw new ArgumentNullException("rootServiceScopeFactory"); + } + + _next = next; + _rootServiceProvider = rootServiceProvider; + _rootServiceScopeFactory = rootServiceScopeFactory; + _rootHttpContextAccessor = rootHttpContextAccessor; + + _rootHttpContextAccessor.SetContextSource(AccessRootHttpContext, ExchangeRootHttpContext); + } + + private HttpContext AccessRootHttpContext() + { +#if NET45 + return CallContext.LogicalGetData(LogicalDataKey) as HttpContext; +#else + throw new NotImplementedException() +#endif + } + + private HttpContext ExchangeRootHttpContext(HttpContext httpContext) + { +#if NET45 + var prior = CallContext.LogicalGetData(LogicalDataKey) as HttpContext; + CallContext.LogicalSetData(LogicalDataKey, httpContext); + return prior; +#else + throw new NotImplementedException() +#endif + } + + public async Task Invoke(HttpContext httpContext) + { + if (httpContext.RequestServices != null) + { + throw new Exception("TODO: nested request container scope? this is probably a mistake on your part?"); + } + + var priorApplicationServices = httpContext.ApplicationServices; + var priorRequestServices = httpContext.RequestServices; + + var appServiceProvider = _rootServiceProvider; + var appServiceScopeFactory = _rootServiceScopeFactory; + var appHttpContextAccessor = _rootHttpContextAccessor; + + if (priorApplicationServices != null && + priorApplicationServices != appServiceProvider) + { + appServiceProvider = priorApplicationServices; + appServiceScopeFactory = priorApplicationServices.GetService(); + appHttpContextAccessor = priorApplicationServices.GetService>(); + } + + using (var scope = appServiceScopeFactory.CreateScope()) + { + var scopeServiceProvider = scope.ServiceProvider; + var scopeHttpContextAccessor = scopeServiceProvider.GetService>(); + + httpContext.ApplicationServices = appServiceProvider; + httpContext.RequestServices = scopeServiceProvider; + + var priorAppHttpContext = appHttpContextAccessor.ExchangeValue(httpContext); + var priorScopeHttpContext = scopeHttpContextAccessor.ExchangeValue(httpContext); + + try + { + await _next.Invoke(httpContext); + } + finally + { + scopeHttpContextAccessor.ExchangeValue(priorScopeHttpContext); + appHttpContextAccessor.ExchangeValue(priorAppHttpContext); + + httpContext.RequestServices = priorRequestServices; + httpContext.ApplicationServices = priorApplicationServices; + } + } + } + } +} diff --git a/src/Microsoft.AspNet.RequestContainer/project.json b/src/Microsoft.AspNet.RequestContainer/project.json new file mode 100644 index 0000000000..b89873123e --- /dev/null +++ b/src/Microsoft.AspNet.RequestContainer/project.json @@ -0,0 +1,23 @@ +{ + "version": "0.1-alpha-*", + "dependencies": { + "Microsoft.AspNet.DependencyInjection": "0.1-alpha-*", + "Microsoft.AspNet.Abstractions": "0.1-alpha-*" + }, + "configurations": { + "net45": {}, + "k10": { + "dependencies": { + "System.Collections": "4.0.0.0", + "System.ComponentModel": "4.0.0.0", + "System.Diagnostics.Debug": "4.0.10.0", + "System.Linq": "4.0.0.0", + "System.Reflection": "4.0.10.0", + "System.Runtime": "4.0.20.0", + "System.Runtime.Extensions": "4.0.10.0", + "System.Threading": "4.0.0.0", + "System.Threading.Tasks": "4.0.0.0" + } + } + } +} \ No newline at end of file