I am using SimpleInjector for my DI in Mvc Core and I have a class that accepts ISession at the constructor.
public SessionAppAdminAuthorization(ISession session)
I need to register this at the DI configuration in StartUp.Configure
method but I don't know how the get the scoped session variable.
container.Register<IAppAdminAuthorization>(() => {
return new SessionAppAdminAuthorization([I Need the ISession]); },
Lifestyle.Scoped);
ASP.NET Core's ISession
can be accessed through the HttpContext.Session
property. Since HttpContext
is runtime data, the Session
is as well. Runtime data should not be injected into your components' constructors, so your SessionAppAdminAuthorization
should not depend on ISession
directly.
The simplest fix is to let SessionAppAdminAuthorization
depend on IHttpContextAccessor
instead and call IHttpContextAccessor.HttpContext.Session
later on. Example:
public class SessionAppAdminAuthorization : IAppAdminAuthorization
{
private readonly IHttpContextAccessor accessor;
public SessionAppAdminAuthorization(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public void DoSomethingUseful() {
if (this.accessor.HttpContext.Session.GetBoolean("IsAdmin")) {
// ...
} else {
// ...
}
}
}
Now you can make the registrations as follows:
public void ConfigureServices(IServiceCollection services)
{
// You need to register IHttpContextAccessor.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment e, ILoggerFactory f)
{
container.RegisterSingleton(
app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
container.Register<IAppAdminAuthorization, SessionAppAdminAuthorization>();
// ...
}
Although this will effectively solve your problem, you might want to take it up one step. In general it's better to hide framework components and abstractions like IHttpContextAccessor
, HttpContext
and ISession
from application components. Instead the Dependency Inversion Principle guides us towards application-specific abstractions implemented by adapters that allow translating these application-specific calls onto framework components. For instance:
// Application-specific abstraction (part of your application's core layer)
public interface IUserContext
{
bool IsAdmin { get; }
}
// Adapter implementation (placed in the Composition Root of your web app)
public class AspNetSessionUserContextAdapter : IUserContext
{
private readonly IHttpContextAccessor accessor;
public AspNetSessionUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public bool IsAdmin => this.accessor.HttpContext.Session.GetBoolean("IsAdmin");
}
// Improved version of SessionAppAdminAuthorization
public class SessionAppAdminAuthorization : IAppAdminAuthorization
{
private readonly IUserContext userContext;
// This class can now be moved to the business layer, since there's no
// more dependency on ASP.NET.
public SessionAppAdminAuthorization(IUserContext userContext) {
this.userContext = userContext;
}
public void DoSomethingUseful() {
if (this.userContext.IsAdmin) {
// ...
} else {
// ...
}
}
}
Registration:
public void Configure(IApplicationBuilder app, IHostingEnvironment e, ILoggerFactory f)
{
var accesr = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
container.RegisterSingleton<IUserContext>(new AspNetSessionUserContextAdapter(accesr));
container.Register<IAppAdminAuthorization, SessionAppAdminAuthorization>();
// ...
}