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

HttpContext.Current is null exception #15

Open
jrmoreno1 opened this issue Jul 19, 2020 · 10 comments
Open

HttpContext.Current is null exception #15

jrmoreno1 opened this issue Jul 19, 2020 · 10 comments

Comments

@jrmoreno1
Copy link
Contributor

jrmoreno1 commented Jul 19, 2020

I am getting an exception after calling the AppplicationDependencyInjectionBuilder.Build, and I can't figure out what I have setup wrong. I trimmed the ConfigurationServices method down to nothing (just a return), but that doesn't help.

[InvalidOperationException: HttpContext.Current is null.]
   AspNetDependencyInjection.Internal.DependencyInjectionWebObjectActivator.GetServiceProviderForCurrentHttpContext() in C:\git\me\Unity.WebForms\Unity.WebForms\AspNetDependencyInjection\AspNetDependencyInjection\DependencyInjectionWebObjectActivator.cs:32
   AspNetDependencyInjection.ObjectFactoryCache.GetRequiredService(Func`1 getServiceProvider, Type serviceType, Boolean useOverrides) in C:\git\me\Unity.WebForms\Unity.WebForms\AspNetDependencyInjection\AspNetDependencyInjection\ObjectFactoryCache.cs:52
   AspNetDependencyInjection.Internal.DependencyInjectionWebObjectActivator.GetService(Type serviceType) in C:\git\me\Unity.WebForms\Unity.WebForms\AspNetDependencyInjection\AspNetDependencyInjection\DependencyInjectionWebObjectActivator.cs:25
   System.Web.HttpRuntime.CreatePublicInstanceByWebObjectActivator(Type type) +9949383
   System.Web.Compilation.BuildManager.AddFolderLevelBuildProviders(BuildProviderSet buildProviders, VirtualPath virtualPath, FolderLevelBuildProviderAppliesTo appliesTo, CompilationSection compConfig, ICollection referencedAssemblies) +156
   System.Web.Compilation.CodeDirectoryCompiler.ProcessDirectoryRecursive(VirtualDirectory vdir, Boolean topLevel) +10225944
   System.Web.Compilation.CodeDirectoryCompiler.FindBuildProviders() +47
   System.Web.Compilation.CodeDirectoryCompiler.GetCodeDirectoryAssembly(VirtualPath virtualDir, CodeDirectoryType dirType, String assemblyName, StringSet excludedSubdirectories, Boolean isDirectoryAllowed) +505
   System.Web.Compilation.BuildManager.CompileCodeDirectory(VirtualPath virtualDir, CodeDirectoryType dirType, String assemblyName, StringSet excludedSubdirectories) +131
   System.Web.Compilation.BuildManager.CompileWebRefDirectory() +34
   System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +247

[HttpException (0x80004005): HttpContext.Current is null.]
   System.Web.Compilation.BuildManager.ReportTopLevelCompilationException() +65
   System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +458
   System.Web.Compilation.BuildManager.CallAppInitializeMethod() +35
   System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException) +605

[HttpException (0x80004005): HttpContext.Current is null.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +10080656
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +99
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +263
@daiplusplus
Copy link
Owner

daiplusplus commented Aug 2, 2020

The static HttpContext.Current property returns null when the calling-thread is not the same thread that was associated with the current HTTP request/response, because HttpContext.Current uses thread-local storage. As you can imagine this means that using async/await (with ConfigureAwait(false)) or using any kind of background thread or thread-pool thread will encounter HttpContext.Current == null.

...however the stack-trace doesn't show any signs of it being an async continunation or background-thread, but it's coming from the build-manager instead, hmm.

Can you please post your actual (and entire) ConfigureServices method? And your entire web.config file?

Thank you.

@daiplusplus
Copy link
Owner

@jrmoreno1 any update? Thanks!

@jrmoreno1
Copy link
Contributor Author

This was a a quick exploration on my own time with a work project and for consistency we are going to be using AutoFaq, so I no longer have a project in the exact state I had at the time. I think you are right about it being a threading issue, possibly a conflict with some Owin libraries we are using. I will look into it some more when I can and get back to you.

@daiplusplus
Copy link
Owner

Sure - I'll leave this issue open until you're able to find out more information.

Though my money is on something in your project's code that causes an async continuation on the wrong thread or code that tries to use ASP.NET from a non-request thread - possibly something in Global.asax.

@jrmoreno1
Copy link
Contributor Author

I haven't forgotten about this, I have actually made some progress: I now have an empty project that exhibits the behavior, which I have been able to move over into a local git repository. I have an empty global.asax file, and an empty ConfigureServices method. It'll probably be a while before I can reduce it down to something a bit more concrete, but I'm fairly sure I'll get there eventually.

@daiplusplus
Copy link
Owner

@jrmoreno1 Boop?

@yegorandrosov
Copy link

I have the same issue with the same stacktrace. My project was converted from Website project to Web application project type

I can't reproduce it on freshly created project though. And I can't share full sources of the project too. But I can do some research for you if you know where to dig. My DI setup code, as you can see error occurs even for container without registered types.


using AspNetDependencyInjection;
using AspNetDependencyInjection.Configuration;
using AspNetDependencyInjection.Services;

using Microsoft.Extensions.DependencyInjection;

using Test.Web.Portal.App_Start;

using WebActivatorEx;

[assembly: PreApplicationStartMethod(typeof(UnityWebFormsStart), methodName: nameof(UnityWebFormsStart.PreStart))]
[assembly: PostApplicationStartMethod(typeof(UnityWebFormsStart), methodName: nameof(UnityWebFormsStart.PostStart))]
[assembly: ApplicationShutdownMethod(typeof(UnityWebFormsStart), methodName: nameof(UnityWebFormsStart.ApplicationShutdown))]
namespace Test.Web.Portal.App_Start
{
	internal static class UnityWebFormsStart
	{
		private static ApplicationDependencyInjection _di; // This is disposed in `ApplicationShutdown()`.

		/// <summary>Invoked when the ASP.NET application starts up, before Global's Application_Start method runs. Dependency-injection should be configured here.</summary>
		internal static void PreStart()
		{
			System.Diagnostics.Debug.WriteLine(nameof(UnityWebFormsStart) + "." + nameof(PreStart) + "() called.");

			// If you are using ASP.NET MVC, regardless of whether you're using ASP.NET Web Forms or the ASPX View Engine, use `.AddMvcDependencyResolver()` (you will need to reference the `AspNetDependencyInjection.Mvc` package/assembly first).

			_di = new ApplicationDependencyInjectionBuilder()
				.ConfigureServices(ConfigureServices)
				.Build();
		}

		private static void ConfigureServices(IServiceCollection services)
		{
			// TODO: Add any dependencies needed here
			_ = services;
				//.AddScoped(x => DatabaseHepler.GetDatabaseData());
		}

		internal static void PostStart()
		{
			System.Diagnostics.Debug.WriteLine(nameof(UnityWebFormsStart) + "." + nameof(PostStart) + "() called.");
		}

		internal static void ApplicationShutdown()
		{
			System.Diagnostics.Debug.WriteLine(nameof(UnityWebFormsStart) + "." + nameof(ApplicationShutdown) + "() called.");

			_di.Dispose();
		}
	}
}

@yegorandrosov
Copy link

Guess it has something to do with dynamic compilation process Website project type has. Web application might do the same thing since when I removed most of the folders from my project, HttpContext.Current was not null anymore, but getting another error.


Line 70: 
Line 71: 			// These conditions should never happen, but just-in-case:
Line 72: 			if( httpContext.Request                            is null ) throw new ArgumentException( message: "httpContext.Request is null."                           , paramName: nameof( httpContext ) );
Line 73: 			if( httpContext.Request.RequestContext             is null ) throw new ArgumentException( message: "httpContext.Request.RequestContext is null."            , paramName: nameof( httpContext ) );
Line 74: 			if( httpContext.Request.RequestContext.HttpContext is null ) throw new ArgumentException( message: "httpContext.Request.RequestContext.HttpContext is null.", paramName: nameof( httpContext ) );
[HttpException (0x80004005): Request is not available in this context] 

System.Web.HttpContext.get_Request() +73   
 AspNetDependencyInjection.AndiExtensions.GetHttpContextBase(HttpContext httpContext) in C:\Users\yandrosov\source\repos\AspNetDependencyInjection\AspNetDependencyInjection\AspNetDependencyInjection\Extensions.cs:72   
AspNetDependencyInjection.ApplicationDependencyInjection.GetServiceProviderForHttpContext(HttpContext httpContext) in C:\Users\yandrosov\source\repos\AspNetDependencyInjection\AspNetDependencyInjection\AspNetDependencyInjection\ApplicationDependencyInjection.cs:180    
AspNetDependencyInjection.Internal.DependencyInjectionWebObjectActivator.GetServiceProviderForCurrentHttpContext() in C:\Users\yandrosov\source\repos\AspNetDependencyInjection\AspNetDependencyInjection\AspNetDependencyInjection\DependencyInjectionWebObjectActivator.cs:34   
 AspNetDependencyInjection.ObjectFactoryCache.GetRequiredService(Func`1 getServiceProvider, Type serviceType, Boolean useOverrides) in C:\Users\yandrosov\source\repos\AspNetDependencyInjection\AspNetDependencyInjection\AspNetDependencyInjection\ObjectFactoryCache.cs:54   
 AspNetDependencyInjection.Internal.DependencyInjectionWebObjectActivator.GetService(Type serviceType) in C:\Users\yandrosov\source\repos\AspNetDependencyInjection\AspNetDependencyInjection\AspNetDependencyInjection\DependencyInjectionWebObjectActivator.cs:25    
System.Web.HttpRuntime.CreateNonPublicInstanceByWebObjectActivator(Type type) +53    
System.Web.HttpApplication.BuildIntegratedModuleCollection(List`1 moduleList) +173    
System.Web.HttpApplication.InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) +785    
System.Web.HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context) +153    
System.Web.HttpApplicationFactory.GetApplicationInstance(HttpContext context) +118    
System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +260

@philparker57
Copy link

A note on this in case what I am seeing matches with the source of the above reported issue.

I was receiving the same error with the same call stack.

In order to attempt to diagnose the issue, I added a class which wraps the IServiceProvider registered by this nuget package on HttpRuntime.WebObjectActivator.

i.e. after the call to the Build() method (chained from the ApplicationDependencyInjectionBuilder)

HttpRuntime.WebObjectActivator = 
    new WebObjectActivatorServiceProviderShim(HttpRuntime.WebObjectActivator);

internal sealed class WebObjectActivatorServiceProviderShim : IServiceProvider
{
    private readonly IServiceProvider _serviceProvider;

    public WebObjectActivatorServiceProviderShim(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public object GetService(Type serviceType)
    {
        System.Diagnostics.Debug.WriteLine($"Attempting to resolve dependency type {serviceType}");

        try
        {
            object result = _serviceProvider.GetService(serviceType);

            if (result == null)
            {
                System.Diagnostics.Debug.WriteLine($"NULL Returned on attempt to resolve dependency type {serviceType}");
            }

            return result;
        }
        catch (Exception exception)
        {
            Debug.WriteLine($"FAILED to resolve dependency type {serviceType} with exception message {exception.Message}");

            throw;
        }
    }
}

The above allowed me to identify that the type which was failing to be resolved (repeatedly) was: System.Data.Services.BuildProvider.DataServiceBuildProvider

According to the MSDN documentation, this is used for WCF Data Services:
https://learn.microsoft.com/en-us/dotnet/api/system.data.services.buildprovider.dataservicebuildprovider?view=netframework-4.8.1

I have attempted to find a WCF or WCF data services reference in our application, but have been unable to find one. However, ours is an extremely large application, with many indirect references, so it is possible we do have some that I have not been able to identify.

Regardless, I found that if I altered the above catch exception handler, to do the following:

if (serviceType == typeof(System.Data.Services.BuildProvider.DataServiceBuildProvider))
    return new System.Data.Services.BuildProvider.DataServiceBuildProvider();

This resulted in our website being able to execute successfully without this resolution failure causing the app to fail to start.

I realize that elsewhere in this project/repository there is information on adapting WCF to be dependency injection compatible. I will take a look at that next, to see if any suggested WCF changes might also fix this issue (and hopefully more correctly than the hacked up solution I have presently).

Anyway, I thought I'd post the above in case it does turn out to be a common source of the above issue.

One thing I would suggest @daiplusplus is that it might be worth including the typename of the type which fails to be resolved on all resolution failures - that might help narrow down the source of, or type of, resolution failures which occur.

@philparker57
Copy link

philparker57 commented Jun 1, 2023

Ok, I found the issue. At least for my occurrence of the above issue....

It was kind of driving me nuts that when running tests, my previously shown code was logging that the DI container was successfully creating and returning other build provider classes.

I finally, downloaded the source code for the nuget package, and added the project to my solution so that I could debug in and see what was going on, and why it was failing to create the DataServiceBuildProvider build provider type, and yet was succeeding on all the other build providers (e.g. PageBuildProvider, ResXBuildProvider, SourceFileBuildProvider, UserControlBuildProvider etc. etc.).

The reason it succeeds on the other build providers is because the default DefaultDependencyInjectionOverrideService has a list of namespaces which will be excluded, and instead the system will use AspNetDependencyInjection.Internal.ActivatorServiceProvider.Instance to generate the requested build provider.

Here's the thing... This is the list of namespaces which are ignored by default:

String[] aspnetNamespaces = new[]
{
	"Microsoft.WebTools.BrowserLink.*", // VS debugging
	"System.ServiceModel.*", // for WCF
	"System.Web.*", // Note this does include System.Web.Mvc
	"!System.Web.Http.**", // We need to return null for ASP.NET Web API services, methinks?
	"WebActivatorEx.*",
	// obviously don't exclude `Microsoft.*` because that would break Microsoft.Extensions.Logging
};

The vast majority of the build provider classes ARE in the System.Web namespace. Actually they are in the System.Web.Compilation namespace - but that of course will match the "System.Web.*" filter.

But the DataServiceBuildProvider provider is NOT in this namespace.

It is in the namespace "System.Data.Services.BuildProvider", which does not match any of the default excluded namespaces.

Why does this not always cause a failure?? The reason for that is that the code in System.Web appears to only attempt to instantiate the DataServiceBuildProvider build provider when you have a "App_WebReferences" folder in your website. And note, it does not appear to even matter if you have any files in this folder - just the presence of this folder will cause the System.Web code to attempt to create a DataServiceBuildProvider via HttpRuntime.CreatePublicInstanceByWebObjectActivator(type).

So, this combination of factors - the DataServiceBuildProvider not being in an excluded namespace, and the presence of a "App_WebReferences" folder in your website will trigger the issue.

I did also take a look with a decompiler at a known list of subclasses of the "BuildProvider" base class, and there is ONE more (in the list that could be found from the assemblys I had loaded) build provider which would not match the default list of excluded namespaces - which is "System.Xaml.Hosting.XamlBuildProvider" - so if this build provider is attempted to be instantiated it could also cause the same issue on startup.

Anyways, there is an easy fix without needing any change in the current published nuget package...:

In your application, where you are registering your services, add the following line:

private static void ConfigureServices(IServiceCollection services)
{
    services.AddDefaultDependencyInjectionOverrideService(
        excludeAspNetNamespacesFromDI: true,
        additionalExclusions: new string[]
        {
            "System.Data.Services.BuildProvider.*"
        });

     // rest of your service registrations....
}

This AddDefaultDependencyInjectionOverrideService extends the excluded namespaces to include the namespace which will result in the desired exclusion of the DataServiceBuildProvider - so that it too will be successfully created by the system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants