'Why is IHttpContextAccessor null in Program class in .NET Core 6?

I am new to .NET Core 6 and Visual Studio 2022.

I have a sample BreadcrumbConfig class. My aim in this class is to get the UrlHelper to resolve url paths.

In order to do that, you need actionContext which needs the HttpContext.

Here is my class

public  class BreadcrumbConfig
{
    [Microsoft.AspNetCore.Components.Inject]
    public IHttpContextAccessor _httpContextAccessor { get; set; }

    public BreadcrumbConfig(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    private  IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;

    private  bool _initialized;
      
    private  readonly object Lock = new object();
    private  readonly Dictionary<string, List<KeyValuePair<string, string>>> SiteMaps = new Dictionary<string, List<KeyValuePair<string, string>>>();

    // it doesn't make sense to use dependency injection (DI) with a static class. Instead of DI,
    // simply add an initialization method to your static class and pass in the dependency
    public  void RegisterBreadcrumbs()
    {
        if (_initialized)
        {
            return;
        }

        lock (Lock)
        {
            if (_initialized)
            {
                 return;
            }

            _initialized = true;
        }

        // The key here is to get the UrlHelper
        var actionDescriptor = new ActionDescriptor();
        var routes = _httpContextAccessor.HttpContext.GetRouteData();
        var actionContext = new ActionContext(_httpContextAccessor.HttpContext,routes, actionDescriptor);
        var url = new UrlHelper(actionContext);

        UrlActionContext j = new UrlActionContext();
        j.Controller = "Records";
        j.Action = "Index";

        // product-index key means product controller index action
        SiteMaps.Add("records-index", new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("<i class='entypo-home'></i>Home",url.Action(j)),
                new KeyValuePair<string, string>("Records","")
            }); 
    }

    public List<KeyValuePair<string, string>> GetBreadcrumbLinksFor(string key)
    {
        return SiteMaps[key];
    }
}

Now in my program class, I want to call this class.

using AdminPanel.Configuration;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddHttpContextAccessor();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

// BreadcrumbConfig;
new BreadcrumbConfig().RegisterBreadcrumbs();
app.Run();

How do I pass IHttpContextAccessor in the constructor? It is always null ...



Solution 1:[1]

I found a solution using middle ware.

Here is my program class

using AdminPanel.Configuration;
using System.Web.Optimization;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
    //app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Use(async (httpContext, next) =>
{
    try
    {
        BreadcrumbConfig.RegisterBreadcrumbs(httpContext);
        await next();
    }
    finally
    {
        await next();
    }
});
// Register Bundles;
//BundleConfig.RegisterBundles(BundleTable.Bundles);
app.Run();

I changed the other class to

namespace AdminPanel.Configuration
{
    public  static class BreadcrumbConfig
    {
        private static bool _initialized;
        private static readonly object Lock = new object();
        private static readonly Dictionary<string, List<KeyValuePair<string, string>>> SiteMaps  = new Dictionary<string, List<KeyValuePair<string, string>>>();

        //t doesn't make sense to use dependency injection (DI) with a static class. Instead of DI,
        //simply add an initialization method to your static class and pass in the dependency
        public static void RegisterBreadcrumbs(HttpContext context)
        {
            if (_initialized)
            {
                return;
            }
            lock (Lock)
            {
                if (_initialized)
                {
                    return;
                }
                _initialized = true;
            }
            // The key here is to get the UrlHelper
            //You really shouldn’t create a UrlHelper yourself.It’s likely that whatever context you are currently in,
            //there is already an IUrlHelper instance available:
            //ControllerBase.Url inside of controllers.
            //PageModel.Url inside a Razor view.
            //ViewComponent.Url inside a view component.
            //So chances are, that you can just access this.Url to get an URL helper.
            //If you find yourself in a situation where that does not exist, for example when implementing your own service,
            //then you can always inject a IUrlHelperFactory together with the IActionContextAccessor to first retrieve the
            //current action context and then create an URL helper for it.
            //As for what that ActionContext is, it is basically an object that contains various values that identify
            //the current MVC action context in which the current request is being handled. So it contains information
            //about the actual request, the resolved controller and action, or the model state about the bound model
            //object.It is basically an extension to the HttpContext, also containing MVC - specific information.
            //var actionDescriptor  = new ActionDescriptor();
            //var routes = _httpContextAccessor.HttpContext.GetRouteData();
            //var actionContext = new ActionContext(_httpContextAccessor.HttpContext,routes, actionDescriptor);
            //var url = new UrlHelper(actionContext);
            //UrlActionContext j = new UrlActionContext();
            //j.Controller = "Records";
            //j.Action = "Index";
            // records-index key means records controller index action
            //SiteMaps.Add("records-index", new List<KeyValuePair<string, string>>
            //{
            //    new KeyValuePair<string, string>("<i class='entypo-home'></i>Home",url.Action(j)),
            //    new KeyValuePair<string, string>("Records","")
            //}); ;
        }
        public static List<KeyValuePair<string, string>> GetBreadcrumbLinksFor(string key)
        {
            return SiteMaps[key];
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 user123456