why doesn't my HttpContext.Request Property not work? [duplicate] - c#

I need to access current HttpContext in a static method or a utility service.
With classic ASP.NET MVC and System.Web, I would just use HttpContext.Current to access the context statically. But how do I do this in ASP.NET Core?

HttpContext.Current doesn't exist anymore in ASP.NET Core but there's a new IHttpContextAccessor that you can inject in your dependencies and use to retrieve the current HttpContext:
public class MyComponent : IMyComponent
{
private readonly IHttpContextAccessor _contextAccessor;
public MyComponent(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string GetDataFromSession()
{
return _contextAccessor.HttpContext.Session.GetString(*KEY*);
}
}

Necromancing.
YES YOU CAN
Secret tip for those migrating large junks chunks (sigh, Freudian slip) of code.
The following method is an evil carbuncle of a hack which is actively engaged in carrying out the express work of satan (in the eyes of .NET Core framework developers), but it works:
In public class Startup
add a property
public IConfigurationRoot Configuration { get; }
And then add a singleton IHttpContextAccessor to DI in ConfigureServices.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
Then in Configure
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
)
{
add the DI Parameter IServiceProvider svp, so the method looks like:
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
,IServiceProvider svp)
{
Next, create a replacement class for System.Web:
namespace System.Web
{
namespace Hosting
{
public static class HostingEnvironment
{
public static bool m_IsHosted;
static HostingEnvironment()
{
m_IsHosted = false;
}
public static bool IsHosted
{
get
{
return m_IsHosted;
}
}
}
}
public static class HttpContext
{
public static IServiceProvider ServiceProvider;
static HttpContext()
{ }
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
// var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));
// Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
// context.Response.WriteAsync("Test");
return context;
}
}
} // End Class HttpContext
}
Now in Configure, where you added the IServiceProvider svp, save this service provider into the static variable "ServiceProvider" in the just created dummy class System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)
and set HostingEnvironment.IsHosted to true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
this is essentially what System.Web did, just that you never saw it (I guess the variable was declared as internal instead of public).
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
ServiceProvider = svp;
System.Web.HttpContext.ServiceProvider = svp;
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest
, CookieHttpOnly=false
});
Like in ASP.NET Web-Forms, you'll get a NullReference when you're trying to access a HttpContext when there is none, such as it used to be in Application_Start in global.asax.
I stress again, this only works if you actually added
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
like I wrote you should.
Welcome to the ServiceLocator pattern within the DI pattern ;)
For risks and side effects, ask your resident doctor or pharmacist - or study the sources of .NET Core at github.com/aspnet, and do some testing.
Perhaps a more maintainable method would be adding this helper class
namespace System.Web
{
public static class HttpContext
{
private static Microsoft.AspNetCore.Http.IHttpContextAccessor m_httpContextAccessor;
public static void Configure(Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor)
{
m_httpContextAccessor = httpContextAccessor;
}
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
return m_httpContextAccessor.HttpContext;
}
}
}
}
And then calling HttpContext.Configure in Startup->Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
System.Web.HttpContext.Configure(app.ApplicationServices.
GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>()
);

The most legit way I came up with was by injecting IHttpContextAccessor in your static implementation as follow:
public static class HttpHelper
{
private static IHttpContextAccessor _accessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_accessor = httpContextAccessor;
}
public static HttpContext HttpContext => _accessor.HttpContext;
}
Then assigning the IHttpContextAccessor in the Startup Configure should do the job.
HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
I guess you should also need to register the service singleton:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Just to add to the other answers...
In ASP.NET Core 2.1, there's the AddHttpContextAccessor extension method, that will register the IHttpContextAccessor with the correct lifetime:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// Other code...
}
}

According to this article: Accessing HttpContext outside of framework components in ASP.NET Core
namespace System.Web
{
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
}
Then:
public static class StaticHttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
{
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
System.Web.HttpContext.Configure(httpContextAccessor);
return app;
}
}
Then:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app)
{
app.UseStaticHttpContext();
app.UseMvc();
}
}
You can use it like this:
using System.Web;
public class MyService
{
public void DoWork()
{
var context = HttpContext.Current;
// continue with context instance
}
}

In Startup
services.AddHttpContextAccessor();
In Controller
public class HomeController : Controller
{
private readonly IHttpContextAccessor _context;
public HomeController(IHttpContextAccessor context)
{
_context = context;
}
public IActionResult Index()
{
var context = _context.HttpContext.Request.Headers.ToList();
return View();
}
}

To access to the session object from a class without explicitly use dependency injection in class constructor follow the next steps:
Add a Singleton instance on Startup.cs (ConfigureServices):
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
In your target class declare an instance of HttpContextAccessor:
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
Access to the session object :
string mySessionVar = _httpContextAccessor.HttpContext.Session.GetString("_MySessionVar");
EXAMPLE
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
YourClass.cs
public class YourClass {
public string yourProperty {
get{
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
return _httpContextAccessor.HttpContext.Session.GetString("_YourSessionVar");
}
}
}
Enjoy :)

Related

Use DI in ConfigureServices?

I have implemented a custom InputFormatter (MyInputFormatter):
public class MyInputFormatter : SystemTextJsonInputFormatter
{
private readonly IMyDepenency _mydependency;
public CustomInputFormatter(
JsonOptions options,
ILogger<SystemTextJsonInputFormatter> logger,
IMyDependency myDependency
) : base(options, logger)
{
_mydependency = myDependency ?? throw new ArgumentNullException(nameof(myDependency));
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
//...
}
}
Now, according to the documentation I need to use it as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new MyInputFormatter(...));
});
}
However, as you can see my CustomInputFormatter has some constructor arguments required and needs some services and it's not clear to me how to use DI to resolve these services. I have read through a lot of answers/blogs/pages like this one but either the inputformatter doesn't have any constructor arguments (and so no need for DI, just new up a new instance inline) or the following is suggested:
public void ConfigureServices(IServiceCollection services)
{
var sp = services.BuildServiceProvider();
services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new MyInputFormatter(
sp.GetService<...>(),
sp.GetService<...>(),
sp.GetService<IMyDependency>(),
));
});
}
But we're not supposed to call BuildServiceProvider from ConfigureServices.
How would I go about this?
You can make use of the Options infrastructure and create an IConfigureOptions<MvcOptions> . This new service can take the necessary dependeices. It will be instantiated and "executed" the first time something (the MVC infrastructure) requests an IOptions<MvcOptions>
public class ConfigureMvcOptionsFormatters : IConfigureOptions<MvcOptions>
{
private readonly ILoggerFactory _factory;
private readonly JsonOptions _jsonOpts;
private readonly IMyDependency _depend;
public ConfigureMvcOptionsFormatters(IOptions<JsonOptions> options, ILoggerFactory loggerFactory, IMyDependency myDependency)
{
_factory = loggerFactory;
_jsonOpts = options.Value;
_depend = myDependency;
}
public void Configure(MvcOptions options)
{
var logger = _factory.CreateLogger<SystemTextJsonInputFormatter>();
var formatter = new MyInputFormatter(_jsonOpts, logger, _depend);
options.InputFormatters.Insert(0, formatter);
}
}
You then register your class to have its IConfigureOptions implementation run by calling the ConfigureOptions<T>() extension method on the IServiceCollection:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.ConfigureOptions<ConfigureMvcOptionsFormatters>();
}
Alternatively you can use an Options builder along with Configure and it's callback, specifying your dependencies as the generic arguments.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddOptions<MvcOptions>()
.Configure<IOptions<JsonOptions>, ILoggerFactory, IMyDependency>(
(o, j, l, d) => o.InputFormatters.Insert(0, new MyInputFormatter(j.Value, l.CreateLogger<SystemTextJsonInputFormatter>(), d)
);
}
Note: I've been using ILoggerFactory because I don't believe the infrastructure will inject an ILogger<ClassA> into ClassB. However, I admit I've never tried it and I'm not near a computer to verify. If it is in fact allowed, you can specify the type you need directly instead.

How to access httpcontext in .net core class library [duplicate]

I need to access current HttpContext in a static method or a utility service.
With classic ASP.NET MVC and System.Web, I would just use HttpContext.Current to access the context statically. But how do I do this in ASP.NET Core?
HttpContext.Current doesn't exist anymore in ASP.NET Core but there's a new IHttpContextAccessor that you can inject in your dependencies and use to retrieve the current HttpContext:
public class MyComponent : IMyComponent
{
private readonly IHttpContextAccessor _contextAccessor;
public MyComponent(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string GetDataFromSession()
{
return _contextAccessor.HttpContext.Session.GetString(*KEY*);
}
}
Necromancing.
YES YOU CAN
Secret tip for those migrating large junks chunks (sigh, Freudian slip) of code.
The following method is an evil carbuncle of a hack which is actively engaged in carrying out the express work of satan (in the eyes of .NET Core framework developers), but it works:
In public class Startup
add a property
public IConfigurationRoot Configuration { get; }
And then add a singleton IHttpContextAccessor to DI in ConfigureServices.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
Then in Configure
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
)
{
add the DI Parameter IServiceProvider svp, so the method looks like:
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
,IServiceProvider svp)
{
Next, create a replacement class for System.Web:
namespace System.Web
{
namespace Hosting
{
public static class HostingEnvironment
{
public static bool m_IsHosted;
static HostingEnvironment()
{
m_IsHosted = false;
}
public static bool IsHosted
{
get
{
return m_IsHosted;
}
}
}
}
public static class HttpContext
{
public static IServiceProvider ServiceProvider;
static HttpContext()
{ }
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
// var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));
// Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
// context.Response.WriteAsync("Test");
return context;
}
}
} // End Class HttpContext
}
Now in Configure, where you added the IServiceProvider svp, save this service provider into the static variable "ServiceProvider" in the just created dummy class System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)
and set HostingEnvironment.IsHosted to true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
this is essentially what System.Web did, just that you never saw it (I guess the variable was declared as internal instead of public).
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
ServiceProvider = svp;
System.Web.HttpContext.ServiceProvider = svp;
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest
, CookieHttpOnly=false
});
Like in ASP.NET Web-Forms, you'll get a NullReference when you're trying to access a HttpContext when there is none, such as it used to be in Application_Start in global.asax.
I stress again, this only works if you actually added
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
like I wrote you should.
Welcome to the ServiceLocator pattern within the DI pattern ;)
For risks and side effects, ask your resident doctor or pharmacist - or study the sources of .NET Core at github.com/aspnet, and do some testing.
Perhaps a more maintainable method would be adding this helper class
namespace System.Web
{
public static class HttpContext
{
private static Microsoft.AspNetCore.Http.IHttpContextAccessor m_httpContextAccessor;
public static void Configure(Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor)
{
m_httpContextAccessor = httpContextAccessor;
}
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
return m_httpContextAccessor.HttpContext;
}
}
}
}
And then calling HttpContext.Configure in Startup->Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
System.Web.HttpContext.Configure(app.ApplicationServices.
GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>()
);
The most legit way I came up with was by injecting IHttpContextAccessor in your static implementation as follow:
public static class HttpHelper
{
private static IHttpContextAccessor _accessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_accessor = httpContextAccessor;
}
public static HttpContext HttpContext => _accessor.HttpContext;
}
Then assigning the IHttpContextAccessor in the Startup Configure should do the job.
HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
I guess you should also need to register the service singleton:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Just to add to the other answers...
In ASP.NET Core 2.1, there's the AddHttpContextAccessor extension method, that will register the IHttpContextAccessor with the correct lifetime:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// Other code...
}
}
According to this article: Accessing HttpContext outside of framework components in ASP.NET Core
namespace System.Web
{
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
}
Then:
public static class StaticHttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
{
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
System.Web.HttpContext.Configure(httpContextAccessor);
return app;
}
}
Then:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app)
{
app.UseStaticHttpContext();
app.UseMvc();
}
}
You can use it like this:
using System.Web;
public class MyService
{
public void DoWork()
{
var context = HttpContext.Current;
// continue with context instance
}
}
In Startup
services.AddHttpContextAccessor();
In Controller
public class HomeController : Controller
{
private readonly IHttpContextAccessor _context;
public HomeController(IHttpContextAccessor context)
{
_context = context;
}
public IActionResult Index()
{
var context = _context.HttpContext.Request.Headers.ToList();
return View();
}
}
To access to the session object from a class without explicitly use dependency injection in class constructor follow the next steps:
Add a Singleton instance on Startup.cs (ConfigureServices):
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
In your target class declare an instance of HttpContextAccessor:
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
Access to the session object :
string mySessionVar = _httpContextAccessor.HttpContext.Session.GetString("_MySessionVar");
EXAMPLE
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
YourClass.cs
public class YourClass {
public string yourProperty {
get{
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
return _httpContextAccessor.HttpContext.Session.GetString("_YourSessionVar");
}
}
}
Enjoy :)

Ninject injection in controller is null

I have a web api and I have to use Ninject to do the DI. I followed the steps in the following tutorial, but still didn't manage to get it to work.
Most of the solutions I found were for old ASP.Net, which didn't work in ASP.Net Core.
And I understand that there's a difference between MVC projects and web-API projects.
https://dev.to/cwetanow/wiring-up-ninject-with-aspnet-core-20-3hp
The startup
public class Startup
{
private readonly AsyncLocal<Scope> scopeProvider = new AsyncLocal<Scope>();
public IKernel Kernel { get; set; }
private object Resolve(Type type) => Kernel.Get(type);
private object RequestScope(IContext context) => scopeProvider.Value;
public Startup(Microsoft.Extensions.Configuration.IConfiguration configuration)
{
Configuration = configuration;
}
public Microsoft.Extensions.Configuration.IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddRequestScopingMiddleware(() => scopeProvider.Value = new Scope());
services.AddCustomControllerActivation(Resolve);
services.AddCustomViewComponentActivation(Resolve);
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
Kernel = RegisterApplicationComponents(app);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
private IKernel RegisterApplicationComponents(IApplicationBuilder app)
{
// IKernelConfiguration config = new KernelConfiguration();
Kernel = new StandardKernel(new ApplicationBusinessLogicModule(),
new DataAccessModule());
// Register application services
foreach (var ctrlType in app.GetControllerTypes())
{
Kernel.Bind(ctrlType).ToSelf().InScope(RequestScope);
}
// Here i do some more bindings
Kernel.BindToMethod(app.GetRequestService<IViewBufferScope>);
return Kernel;
}
private sealed class Scope : DisposableObject { }
}
public static class BindingHelpers
{
public static void BindToMethod<T>(this IKernel config, Func<T> method) =>
config.Bind<T>().ToMethod(c => method());
}
The controller:
[ApiController]
public class GpsController : Controller
{
[Inject]
public IGPSProcessor Processor;
[HttpPost("[Action]")]
public XmlDocument Gpsehi([FromBody]string message)
{
return Processor.Run(message);
}
}
The property in the controller is always null, it shouldn't be null.
As per documentation Ninject no longer supports field injections. Convert your field to property with a public setter and you should be good to go
[Inject]
public IGPSProcessor Processor { private get; set; }

Access the current HttpContext in ASP.NET Core with Custom

I am writing code for following: Access the current HttpContext in ASP.NET Core
I am receiving error. How would I resolve this?
Also, whats the code for Interface IMyComponent? Just want to be sure its correct.
Errors:
Type or namespace IMyComponent Cannot be found
The Name 'KEY' does not exist in current context.
public class MyComponent : IMyComponent
{
private readonly IHttpContextAccessor _contextAccessor;
public MyComponent(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string GetDataFromSession()
{
return _contextAccessor.HttpContext.Session.GetString(*KEY*);
}
}
Some points you need to pay attention to:
1.You class inherit from an interface and implement a GetDataFromSession method.You need to define an interface IMyComponent first and register IMyComponent in staryup if you would like use by DI
public interface IMyComponent
{
string GetDataFromSession();
}
startup.cs
services.AddSingleton<IMyComponent, MyComponent>();
2.It seems that you would like to get data from session. The "Key" represents any session name (string).You need to enable session for asp.net core and set a session value first.
_contextAccessor.HttpContext.Session.SetString("Key", "value");
3.Register IHttpContextAccessor in your startup
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
4.Full demo:
MyComponent.cs
public class MyComponent : IMyComponent
{
private readonly IHttpContextAccessor _contextAccessor;
public MyComponent(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string GetDataFromSession()
{
_contextAccessor.HttpContext.Session.SetString("Key", "value");
return _contextAccessor.HttpContext.Session.GetString("Key");
}
}
public interface IMyComponent
{
string GetDataFromSession();
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
// Make the session cookie essential
options.Cookie.IsEssential = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IMyComponent, MyComponent>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//other middlewares
app.UseSession();
app.UseMvc();
}
}
API Controller:
public class ForumsController : ControllerBase
{
private readonly IMyComponent _myComponent;
public ForumsController(IMyComponent myComponent)
{
_myComponent = myComponent;
}
// GET api/forums
[HttpGet]
public ActionResult<string> Get()
{
var data = _myComponent.GetDataFromSession();//call method and return "value"
return data;
}

Working with dependency injection in mvc

In one of our app I am already using the dependency injection of AppTenant class like follows
public void ConfigureServices(IServiceCollection services)
{
services.AddMultitenancy<AppTenant, CachingAppTenantResolver>();
services.Configure<MultitenancyOptions>(Configuration.GetSection("Multitenancy"));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseMultitenancy<AppTenant>();
}
and in controller i am able to access it easily as follows
public AccountController(AppTenant tenant)
{
this.tenant = tenant;
}
Now, I want to access the same AppTenant OR HttpContext in other project class in the same solution.
So, I have tried like this
public SqlStringLocalizerFactory(
AppTenant tenant)
{
_tenant = tenant;
}
But it is coming null, so what I need to do, to get the AppTenant OR HttpContext in the other project class ?
For SqlStringLocalizerFactory class the services are written in ConfigureServices method like follows
public static class SqlLocalizationServiceCollectionExtensions
{
public static IServiceCollection AddSqlLocalization(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
return AddSqlLocalization(services, setupAction: null);
}
public static IServiceCollection AddSqlLocalization(
this IServiceCollection services,
Action<SqlLocalizationOptions> setupAction)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.TryAdd(new ServiceDescriptor(
typeof(IStringExtendedLocalizerFactory),
typeof(SqlStringLocalizerFactory),
ServiceLifetime.Singleton));
services.TryAdd(new ServiceDescriptor(
typeof(IStringLocalizerFactory),
typeof(SqlStringLocalizerFactory),
ServiceLifetime.Singleton));
services.TryAdd(new ServiceDescriptor(
typeof(IStringLocalizer),
typeof(SqlStringLocalizer),
ServiceLifetime.Singleton));
if (setupAction != null)
{
services.Configure(setupAction);
}
return services;
}
}
I have even tried with IHttpContextAccessor, but still not getting any success.
Any help on this appreciated !
Edit-2
New Solution:
public SqlStringLocalizerFactory(IHttpContextAccessor _accessor)
{
_accessor= accessor;
}
public void SomeMethod()
{
var tenant = _accessor.HttpContext.RequestServices
.GetRequiredService<AppTenant>();
}
Edit : IServiceProvider way doesn't work as i expect. See #Sock's solution
First, i assumes the problem occurs because of captive dependency as pointed by #qujck. To avoid captive dependency:
If lifetime of SqlStringLocalizerFactory must be singleton(some cases must be), in this
case use IServiceProvider:
public SqlStringLocalizerFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void SomeMethod()
{
var tenant = _serviceProvider.GetService<AppTenant>();
}
Otherwise using AddScoped seems reasonable for your case.
You have two options, the best option, if the SqlStringLocalizerFactory can be a scoped dependency (you get a new instance for every request) then you can register it as a scoped dependency:
services.TryAdd(new ServiceDescriptor(
typeof(IStringLocalizerFactory),
typeof(SqlStringLocalizerFactory),
ServiceLifetime.Scoped));
If the SqlStringLocalizerFactory must be a a Singleton dependency, then you need to make sure you resolve a scoped dependency for the Tenant by using a ServiceSope:
public class SqlStringLocalizerFactory
{
private readonly IServiceProvider _serviceProvider;
public SqlStringLocalizerFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void SomeMethod()
{
using (var serviceScope = _serviceProvider
.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var tenant = serviceScope.ServiceProvider.GetService<AppTenant>();
// do something with tenant...
}
}
}

Categories