0

I'm trying to upgrade a .NET Core 2.1 project to .NET 6.0+.

I see some different behavior in both versions.

Error:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

WebRequestContext.Localization.get returned null.

I would like to understand if is it expected. In the past migrated from ASP.NET MVC on .NET 4.8 to .NET Core 2.1 and used this answer code @Html.Action in ASP.NET Core.

Everything works fine in .NET Core 2.1, but now I'm upgrading the project to .NET 6.0 my project is not working for certain functionality.

Here is my code reference for WebRequestContext:

public class WebRequestContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;
  
    public WebRequestContext(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
        Current = this;
    }

    public static WebRequestContext Current { get; private set; }
  
    public Localization Localization
    {
        get
        {
            return (Localization)GetFromContextStore("Localization");
        }
        set
        {
            AddToContextStore("Localization", value);
        }
    }

    protected object GetFromContextStore(string key)
    {
       return _httpContextAccessor.HttpContext?.Items[key];
    }

    protected object AddToContextStore(string key, object value)
    {
        if (_httpContextAccessor.HttpContext == null) return value;
        _httpContextAccessor.HttpContext.Items[key] = value;
        return value;
    }
}

I have initialized this in ServiceCollection like this.

    services.AddSingleton<WebRequestContext, WebRequestContext>();            
    services.AddHttpContextAccessor();
    services.AddTransient<IActionContextAccessor, ActionContextAccessor>();

In my project many places in the View and also in the HtmlHelperExtensions trying to access this value. At the start of the application, these values are getting stored as expected. I can see this value is accessible at the controller level. In the view if I try to access the @Html.DxaRegion("Header") custom extension action after calling this RenderActionAsync method this original _httpContextAccessor.HttpContext value is getting reset to null. This behavior is not happening in .NET Core 2.1

Because in many places I have used to access Localization, this value actually stored in the _httpContextAccessor.HttpContext.Items[key] getting null.

 WebRequestContext.Current.Localization.CultureInfo

This is the code block setting the current HTTPContext value to null. but that same works fine in .NET Core 2.1:

private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
        // fetching required services for invocation
        var serviceProvider = helper.ViewContext.HttpContext.RequestServices;
        var actionContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IActionContextAccessor>();
        var httpContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IHttpContextAccessor>();
        var actionSelector = serviceProvider.GetRequiredService<IActionSelector>();

        // creating new action invocation context
        var routeData = new RouteData();

        foreach (var router in helper.ViewContext.RouteData.Routers)
        {
            routeData.PushState(router, null, null);
        }

        routeData.PushState(null, new RouteValueDictionary(new { controller = controller, action = action, area = area }), null);
        routeData.PushState(null, new RouteValueDictionary(parameters ?? new { }), null);

        //get the actiondescriptor
        RouteContext routeContext = new RouteContext(helper.ViewContext.HttpContext) { RouteData = routeData };
        var candidates = actionSelector.SelectCandidates(routeContext);
        var actionDescriptor = actionSelector.SelectBestCandidate(routeContext, candidates);

        var originalActionContext = actionContextAccessor.ActionContext;
        var originalhttpContext = httpContextAccessor.HttpContext;

        try
        {
            var newHttpContext = serviceProvider.GetRequiredService<IHttpContextFactory>().Create(helper.ViewContext.HttpContext.Features);

            if (newHttpContext.Items.ContainsKey(typeof(IUrlHelper)))
            {
                newHttpContext.Items.Remove(typeof(IUrlHelper));
            }

            newHttpContext.Response.Body = new MemoryStream();
            var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);
            actionContextAccessor.ActionContext = actionContext;
            var invoker = serviceProvider.GetRequiredService<IActionInvokerFactory>().CreateInvoker(actionContext);
            await invoker.InvokeAsync();
            newHttpContext.Response.Body.Position = 0;

            using (var reader = new StreamReader(newHttpContext.Response.Body))
            {
                return new HtmlString(reader.ReadToEnd());
            }
        }
        catch (Exception ex)
        {
            return new HtmlString(ex.Message);
        }
        finally
        {
            actionContextAccessor.ActionContext = originalActionContext;
            httpContextAccessor.HttpContext = originalhttpContext;

            if (helper.ViewContext.HttpContext.Items.ContainsKey(typeof(IUrlHelper)))
            {
                helper.ViewContext.HttpContext.Items.Remove(typeof(IUrlHelper));
            }
        }
    }
}

Did anyone face a similar issue? Any help would be much appreciated.

Progman
  • 16,827
  • 6
  • 33
  • 48
Velmurugan
  • 406
  • 8
  • 17

1 Answers1

0

Here is the code fixed this problem:

Instead of using this code.

services.AddHttpContextAccessor();

Used this code in order to fix my problem

public class HttpContextAccessor : IHttpContextAccessor
    {
        private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
        HttpContext IHttpContextAccessor.HttpContext { get => _httpContextCurrent.Value; set => _httpContextCurrent.Value = value; }
    }

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Velmurugan
  • 406
  • 8
  • 17