Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DoNotMerge] BranchWithServices: Failing test for resolving IHttpContextAccessor #139

Closed
wants to merge 3 commits into
base: master
from

Conversation

Projects
None yet
2 participants
@damianh
Copy link

damianh commented Jun 6, 2018

Been exploring UseBranchWithServices and not entirely sure am doing it write. Here I added a test project for WebApiContrib.Core and failing test to show resolution of a service failing. Discovered this when attempting to use Identity Server in a branch and was getting ANEs and NREs from deep within it.

It's not clear whether one should be passing the IHttpContextAccessor into the branch's container. And if one should, what else is missing?

Application startup exception: System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Http.IHttpContextAccessor' while attempting to activate 'WebApiContrib.Core.Tests.ParallelApplicationPipelinesExtensionsTests+Service'.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType, ServiceProvider serviceProvider)
   at System.Collections.Concurrent.ConcurrentDictionaryExtensions.GetOrAdd[TKey,TValue,TArg](ConcurrentDictionary`2 dictionary, TKey key, Func`3 valueFactory, TArg arg)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
   at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass4_0.<UseMiddleware>b__0(RequestDelegate next)
   at Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.Build()
   at WebApiContrib.Core.ParallelApplicationPipelinesExtensions.UseBranchWithServices(IApplicationBuilder app, PathString path, Action`1 servicesConfiguration, Action`1 appBuilderConfiguration) in E:\dev\damianh\WebAPIContrib.Core\src\WebApiContrib.Core\ParallelApplicationPipelinesExtensions.cs:line 46
   at WebApiContrib.Core.Tests.ParallelApplicationPipelinesExtensionsTests.Startup.Configure(IApplicationBuilder app) in E:\dev\damianh\WebAPIContrib.Core\tests\WebApiContrib.Core.Tests\WebApiContrib.Core.Tests\ParallelApplicationPipelinesExtensionsTests.cs:line 41
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.Internal.AutoRequestServicesStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Http.IHttpContextAccessor' while attempting to activate 'WebApiContrib.Core.Tests.ParallelApplicationPipelinesExtensionsTests+Service'.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType, ServiceProvider serviceProvider)
   at System.Collections.Concurrent.ConcurrentDictionaryExtensions.GetOrAdd[TKey,TValue,TArg](ConcurrentDictionary`2 dictionary, TKey key, Func`3 valueFactory, TArg arg)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
   at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass4_0.<UseMiddleware>b__0(RequestDelegate next)
   at Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.Build()
   at WebApiContrib.Core.ParallelApplicationPipelinesExtensions.UseBranchWithServices(IApplicationBuilder app, PathString path, Action`1 servicesConfiguration, Action`1 appBuilderConfiguration) in E:\dev\damianh\WebAPIContrib.Core\src\WebApiContrib.Core\ParallelApplicationPipelinesExtensions.cs:line 46
   at WebApiContrib.Core.Tests.ParallelApplicationPipelinesExtensionsTests.Startup.Configure(IApplicationBuilder app) in E:\dev\damianh\WebAPIContrib.Core\tests\WebApiContrib.Core.Tests\WebApiContrib.Core.Tests\ParallelApplicationPipelinesExtensionsTests.cs:line 41
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.Internal.AutoRequestServicesStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
   at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
   at Microsoft.AspNetCore.TestHost.TestServer..ctor(IWebHostBuilder builder, IFeatureCollection featureCollection)
   at WebApiContrib.Core.Tests.ParallelApplicationPipelinesExtensionsTests.<HttpContextAccessor_Not_Null>d__0.MoveNext() in E:\dev\damianh\WebAPIContrib.Core\tests\WebApiContrib.Core.Tests\WebApiContrib.Core.Tests\ParallelApplicationPipelinesExtensionsTests.cs:line 25
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_1.<<InvokeTestMethodAsync>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Xunit.Sdk.ExecutionTimer.<AggregateAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Xunit.Sdk.ExceptionAggregator.<RunAsync>d__9.MoveNext()

damianh added some commits Jun 6, 2018

@damianh damianh force-pushed the damianh:branch-with-services branch from 6ae463e to 4975a75 Jun 6, 2018

@filipw

This comment has been minimized.

Copy link
Member

filipw commented Jun 6, 2018

there is a narrow set of services that are owned by the HostingApplication https://github.com/aspnet/Hosting/blob/cd3f58bed7234ed5035a995c8688dccd6706b584/src/Microsoft.AspNetCore.Hosting/Internal/HostingApplication.cs#L35

HttpContext gets initialized there, and is global for the request, so you cannot have it scoped per branch. I's the application that owns HttpContext, not a branch. Because HttpContextAccessor keeps a local field where HttpContext is stored, it means that HttpContextAccessor must be available in the global DI container for things to work, instead of just in the child (branch-specific) containers.

That is why the code you commented out fixes the test.
You could also simply just move it to the main DI container (under the now empty ConfigureServices).

In the IdSrv case, it normally registers the accessor silently, as its own dependency. But if you do AddIdentityServer() in a branch, it will not be enough because, as explained, the registration of HttpContextAccessor service must be global, and hence the problem you had with the token.

@damianh damianh changed the title BranchWithServices: Failing test for resolving IHttpContextAccessor [DoNotMerge] BranchWithServices: Failing test for resolving IHttpContextAccessor Jun 6, 2018

@damianh

This comment has been minimized.

Copy link
Author

damianh commented Jun 6, 2018

Ok I think I get the explanation why. Which basically means this isn't going to work?

@filipw

This comment has been minimized.

Copy link
Member

filipw commented Jun 6, 2018

no it works, you can issue and consume tokens, build isolated branches as you wish. it just means since your application is effectively a main app + n child apps (service providers), the HttpContext is always owned and initialized from the main (entry) app. I don't think this is unreasonable too, since you get into the branch via the main app anyway

@damianh

This comment has been minimized.

Copy link
Author

damianh commented Jun 8, 2018

Yep understood and agreed. Child app containers will need to reach into the main app container for the HttpContextAccessor but that's where the integration stops.

I think this implementation is superior as it sets up the default common services, works with startup classes and wires in the IHttpContextAccessor if the main ("root") container has the service registered.

Thanks for the discussion and pointer @filipw :)

@damianh damianh closed this Jun 8, 2018

@filipw

This comment has been minimized.

Copy link
Member

filipw commented Jun 8, 2018

excellent, thanks! overall you are absolutely right, this code could easily be improved to handle i.e. Startup classes.
At the moment it is basically a copy-paste from a blog article https://www.strathweb.com/2017/04/running-multiple-independent-asp-net-core-pipelines-side-by-side-in-the-same-application/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.