ASP.NET Core - Dependency Injection - IdentityServer4 DbContext - c#

I've been trying to find what the best/preferred way is to have my RoleService obtain a ConfigurationDbContext (IdentityServer4).
I would really like to decouple so that my RoleService can be testable.
The only way I found get access to the ConfigurationDbContext is via creation of a public static IServiceProvider in Startup.cs:
public class Startup
{
private readonly IHostingEnvironment _environment;
// THIS IS THE PROPERTY I USED
public static IServiceProvider ServiceProvider { get; private set; }
public ConfigurationDbContext GetConfigurationDbContext()
{
return null;
}
public Startup(ILoggerFactory loggerFactory, IHostingEnvironment environment)
{
loggerFactory.AddConsole(LogLevel.Debug);
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
var connectionString = DbSettings.IdentityServerConnectionString;
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// configure identity server with in-memory stores, keys, clients and scopes
var identityServerConfig = services.AddIdentityServer()
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddSigningCredential(new X509Certificate2(Path.Combine(_environment.ContentRootPath, "certs", "IdentityServer4Auth.pfx"), "test"));
identityServerConfig.Services.AddTransient<IResourceOwnerPasswordValidator, ActiveDirectoryPasswordValidator>();
identityServerConfig.Services.AddTransient<IProfileService, CustomProfileService>();
services.AddDbContext<ConfigurationDbContext>(options => options.UseSqlServer(connectionString));
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
ServiceProvider = app.ApplicationServices;
// other code emitted
}
}
And then utilizing this code in RoleService.cs:
public class RoleService : IRoleService
{
public async Task<ApiResource[]> GetApiResourcesByIds(int[] ids)
{
ApiResource[] result;
using (var serviceScope = Startup.ServiceProvider.GetService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
result =
context.ApiResources.Where(x => ids.Contains(x.Id))
.Include(x => x.Scopes)
.Include(x => x.UserClaims)
.ToArray();
return result;
}
}
}
Is this the best way to get a dependency in RoleService.cs?
Is there a way to abstract the serviceScope (since it is in a using statement, and probably IDisposable, I don't really know if there is a way to abstract obtaining a context?
Any other recommendations or best practices?

You can pass a dependency to the RoleService by creating a constructor with the desired dependency as a constructor parameter.
Depending on the lifetime of your RoleService (can't tell based on the provided code) you would pass either the ConfigurationDbContext directly to the RoleService if the service is already Scoped. Or you would pass the IServiceScopeFactory to create a scope manually in your service.
Something like:
private readonly IServiceScopeFactory _scopeFactory;
public RoleService(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public async Task<ApiResource[]> GetApiResourcesByIds(int[] ids)
{
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
}
}
Since your method is thight coupled to the DbContext (which isn't necessary a bad thing) you would probably end up creating an in-memory ConfigurationDbContext.
The IdentityServer4.EntityFramework repository that you use has some examples for testing that here.

Related

Xunit Testing EFcore Repositories InMemory DB

I am trying to unit test the repositories, I am using InMemory option in EFCore . This is the method
[Fact]
public async Task GetCartsAsync_Returns_CartDetail()
{
ICartRepository sut = GetInMemoryCartRepository();
CartDetail cartdetail = new CartDetail()
{
CommercialServiceName = "AAA"
};
bool saved = await sut.SaveCartDetail(cartdetail);
//Assert
Assert.True(saved);
//Assert.Equal("AAA", CartDetail[0].CommercialServiceName);
//Assert.Equal("BBB", CartDetail[1].CommercialServiceName);
//Assert.Equal("ZZZ", CartDetail[2].CommercialServiceName);
}
private ICartRepository GetInMemoryCartRepository()
{
DbContextOptions<SostContext> options;
var builder = new DbContextOptionsBuilder<SostContext>();
builder.UseInMemoryDatabase($"database{Guid.NewGuid()}");
options = builder.Options;
SostContext personDataContext = new SostContext(options);
personDataContext.Database.EnsureDeleted();
personDataContext.Database.EnsureCreated();
return new CartRepository(personDataContext);
}
I am getting error which say
System.TypeLoadException : Method 'ApplyServices' in type
'Microsoft.EntityFrameworkCore.Infrastructure.Internal.InMemoryOptionsExtension' from assembly
'Microsoft.EntityFrameworkCore.InMemory, Version=1.0.1.0, Culture=neutral,
PublicKeyToken=adb9793829ddae60' does not have an implementation.
Microsoft.
EntityFrameworkCore.InMemoryDbContextOptionsExtensions.UseInMemoryDatabase(DbContextOptionsBuilder
optionsBuilder, String databaseName, Action`1 inMemoryOptionsAction)
Microsoft.EntityFrameworkCore.InMemoryDbContextOptionsExtensions.UseInMemoryDatabase[TContext]
(DbContextOptionsBuilder`1 optionsBuilder, String databaseName, Action`1 inMemoryOptionsAction)
My reference is from https://www.carlrippon.com/testing-ef-core-repositories-with-xunit-and-an-in-memory-db/
Please suggest me where i am going wrong with the current implementation . Thanks in Advance
I suggest reading the official Microsoft documentation about integration testing.
https://learn.microsoft.com/fr-fr/aspnet/core/test/integration-tests?view=aspnetcore-3.0
Secondly, I you start adding this kind of boilerplate to create your tests with the memory database you will stop doing it very soon.
For integration tests, you should be near to your development configuration.
Here my configuration files and a usage in my CustomerController :
Integration Startup File
Have all think about database creation and dependency injection
public class IntegrationStartup : Startup
{
public IntegrationStartup(IConfiguration configuration) : base(configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public override void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
services.AddDbContext<StreetJobContext>(options =>
{
options.UseInMemoryDatabase("InMemoryAppDb");
});
//services.InjectServices();
//here you can set your ICartRepository DI configuration
services.AddMvc(option => option.EnableEndpointRouting = false)
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddApplicationPart(Assembly.Load(new AssemblyName("StreetJob.WebApp")));
ConfigureAuthentication(services);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var serviceScopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
using (var serviceScope = serviceScopeFactory.CreateScope())
{
//Here you can add some data configuration
}
app.UseMvc();
}
The fake startup
it's quite similar to the one in the Microsoft documentation
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder(null)
.UseStartup<TStartup>();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseSolutionRelativeContentRoot(Directory.GetCurrentDirectory());
builder.ConfigureAppConfiguration(config =>
{
config.AddConfiguration(new ConfigurationBuilder()
//custom setting file in the test project
.AddJsonFile($"integrationsettings.json")
.Build());
});
builder.ConfigureServices(services =>
{
});
}
}
The controller
public class CustomerControllerTest : IClassFixture<CustomWebApplicationFactory<IntegrationStartup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<IntegrationStartup> _factory;
private readonly CustomerControllerInitialization _customerControllerInitialization;
public CustomerControllerTest(CustomWebApplicationFactory<IntegrationStartup> factory)
{
_factory = factory;
_client = _factory.CreateClient();
}
}
With this kind of setting, testing the integration tests are very similar to the development controller.
It's a quite good configuration for TDD Developers.

Singleton service and EF Core dbContext

The application uses ASP.NET Core 3. At the first call, a project class service is created.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
Project.cs
public class Project
{
private readonly DataBaseContext _dbContext;
public Project(DataBaseContext dbContext)
{
_dbContext = dbContext;
Init();
}
public async void Init()
{
await SomeMethod('text');
}
public async Task SomeMethod(string message)
{
_dbContext.Items.Add(message);
await _dbContext.SaveChangesAsync();
}
}
This is not entirely correct and I want to create a service when the application starts.
public void ConfigureServices(IServiceCollection services)
{
// AddDbContext
Project project = new Project(dbContext); // How to get dbcontext?
services.AddSingleton(typeof(Project), project);
}
How to pass dbcontext in this case?
UPDATE
Now in the Stratup class, I call the init () method of the project service.
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
Project project = serviceProvider.GetService<Project>();
project.Init();
// some code
}
Dunno why would you not use the automatic Dependecy Injection at your first code
Singleton are created upon app start. And as long as the init method is called inside the constructor it will run. So this code will work on your case already
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
But anyway if you insist on instantiating the Project class then you can use this. Get the DBContext using ServiceProvider.
public void ConfigureServices(IServiceCollection services)
{
// AddDbContext
var sp = services.BuildServiceProvider();
var dbContext = sp.GetRequiredService<DbContext>();
Project project = new Project(dbContext);
services.AddSingleton(typeof(Project), project);
}

Using ASP.NET DI for SignalR with TinyIoC

In a project, I'm using Nancy/TinyIoC for Dependency Injection. I had no problems thus far.
I added SignalR to my project and setup my hubs so that I'm injecting IHubContext into my hub.
I'm running into a problem that when TinyIoC tries to resolve one of its dependency trees, it runs into an ASP.NET type and cannot resolve such. How do I work around this? My first guess was to register the type within TinyIoC, but that seems tedious.
Here's what I have:
public class Startup
{
public void Configure(IApplicationBuilder builder)
{
// Register types from ASP.net
// Pass instances to UseNancy
var hubContext = builder.ApplicationServices.GetService<IHubContext<MessageSender>>();
builder
.UseCors(AllowAllOrigins)
.UseSignalR(HubRegistration.RouteRegistrations)
.UseOwin(x => x.UseNancy());
}
public virtual void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(AllowAllOrigins,
builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
services.AddSignalR();
}
}
public class MessageRepo : IRepository<Message>
{
private readonly IDatabase<Message> _database;
private readonly IValidator<Message> _messageValidator;
private readonly IMessageSender<Message> _hubContext;
public MessageRepo(IDatabase<Message> database, IValidator<Message> messageValidator, IMessageSender<Message> hubContext)
{
_database = database;
_messageValidator = messageValidator;
_hubContext = hubContext;
}
}
public class MessageSender : Hub, IMessageSender<Message>
{
public MessageSender(IHubContext<MessageSender> context)
{
_context = context;
}
}

Access HttpContextAccessor from startup.cs in .net Core WebApi

I'm logging exceptions to database in asp.net core. MyDbContext take HttpContextAccessor parameter.So, I'm sending HttpContextAccessor to MyDbContext.cs for access my JWT. But, I can't access my HttpContextAccessor from Startup.cs. How can I achieve this?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddMvc();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddDbContext<MyDbContext>();
services.AddTransient<IUnitOfWork, UnitOfWork>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseExceptionHandler(builder => builder.Run(async context =>
{
var error = context.Features.Get<IExceptionHandlerFeature>();
context.Response.AddApplicationError(error,???????);//I want access HttpContextAccessor
await context.Response.WriteAsync(error.Error.Message);
}));
app.UseHttpsRedirection();
app.UseMvc();
}
ExceptionHelper.cs
public static class ExceptionHelper
{
public static async Task AddApplicationError(this HttpResponse response, IExceptionHandlerFeature error, IHttpContextAccessor httpContextAccessor)
{
Log log = new Log();
log.Message = error.Error.Message;
MyDbContext context = new MyDbContext(null, httpContextAccessor);
UnitOfWork uow = new UnitOfWork(context);
uow.LogRepo.AddOrUpdate(log);
await uow.CompleteAsync(false);
}
}
MyDbContext
public class MyDbContext : DbContext
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyDbContext(DbContextOptions<MyDbContext> options, IHttpContextAccessor httpContextAccessor)
: base(GetOptions())
{
_httpContextAccessor = httpContextAccessor;
}
private static DbContextOptions GetOptions()
{
return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), "server=asd; database=; user id=asd; password=1234").Options;
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var token = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
var audits = AuditHelper.AddAuditLog(base.ChangeTracker, token);
return (await base.SaveChangesAsync(true, cancellationToken));
}
}
You can inject whatever you need into the Configure method. You have already added it to the service collection with this line:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
So all you need to do is add it to the list of arguments on the method like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor accessor)
{
// make use of it here
}
As an aside: I would also point out that it's a bit of a code smell that you are manually creating an instance of your DbContext inside your static helper class when you are using dependency injection.
Update in response to comment
In order to tidy things up a bit I would start by changing your startup to configure you DbContext something like this:
public class Startup
{
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// register other things here...
services.AddDbContext<DataContext>(o => o.UseSqlServer(
config.GetConnectionString("MyConnectionString") // from appsettings.json
));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// set up app here...
}
}
You can then remove the .GetOptions() method from MyDbContext, and change the constructor to:
public MyDbContext(DbContextOptions<MyDbContext> options, IHttpContextAccessor httpContextAccessor)
: base(options)
{
_httpContextAccessor = httpContextAccessor;
}
Then you inject an instance of MyDbContext into whatever class needs access to it. The problem is that (to my knowledge) DI does not work well with static classes/methods, and you are using an extension method on the HttpResponse to log your error.
In my opinion it would be better to create a class that is responsible for logging the error with a dependency on your MyDbContext and have that injected into the Configure method:
public class ErrorLogger
{
private MyDataContext db;
public ErrorLogger(MyDataContext db) => this.db = db;
public void LogError(IExceptionHandlerFeature error)
{
Log log = new Log();
log.Message = error.Error.Message;
UnitOfWork uow = new UnitOfWork(this.db);
uow.LogRepo.AddOrUpdate(log);
await uow.CompleteAsync(false);
}
}
Register it with the DI container as you have with other things, then inject it into Configure instead of the HTTP accessor:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ErrorLogger logger)
{
app.UseExceptionHandler(builder => builder.Run(async context =>
{
var error = context.Features.Get<IExceptionHandlerFeature>();
logger.LogError(error);
await context.Response.WriteAsync(error.Error.Message);
}));
}
I have not tested this, and I am not familiar with .UseExceptionHandler(...) as I use application insights to log exceptions etc (take a look at it if you've not seen it). One thing to be aware of is the scope of your dependencies; your DbContext will be Scoped by default (and I think you should leave it that way), which means you cannot inject it into Singleton objects.

Is the constructor injection of HttpContext a good idea? [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 :)

Categories

Resources