I am building a throwaway application in MVC 6 and experimenting with different architectures for dependencies.
The problem I am facing is how to create a custom 'MyAppContext
' object specific to the Application. This would require some information from the HttpContext
and some information from the database, and will be a request-scoped repository for application specific attributes. I want to pass the instance of the HttpContext
into the constructor of the 'MyAppContext
'.
I have successfully created a 'DataService
' object with an IDataService
interface using DI and this works Ok.
The difference with the 'MyAppContext' class is that it has two parameters in the constructor - the 'DataService
' and the Microsoft.AspNet.Http.HttpContext
. Here is the MyAppContext class:
public class MyAppContext : IMyAppContext
{
public MyAppContext(IDataService dataService, HttpContext httpContext)
{
//do stuff here with the httpContext
}
}
In the startup code, I register the DataService instance and the MyAppContext instance:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//adds a singleton instance of the DataService using DI
services.AddSingleton<IDataService, DataService>();
services.AddScoped<IMyAppContext, MyAppContext>();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseRequestServices();
app.UseMvc(routes => /* routes stuff */);
}
I am expecting the HttpContext
parameter in the constructor to get resolved by DI.
When running the code, this is the exception I get returned:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNet.Http.HttpContext' while attempting to activate 'MyAppContext'
I figure this is because there is no specific instance of HttpContext
that this error is occurring, but I don't know how to register the HttpContext
instance in DI. I added the line 'app.UseRequestServices();
' but this hasn't made any difference. I also tried a variant of:
services.AddScoped<HttpContext, HttpContext>();
But this fails because the second HttpContext
is supposed to be an instance - I know it's not correct but haven't been able to work out what is.
So, in summary - how can I pass in the HttpContext
object into the constructor of MyAppContext?
ASP.NET Core apps access HttpContext through the IHttpContextAccessor interface and its default implementation HttpContextAccessor. It's only necessary to use IHttpContextAccessor when you need access to the HttpContext inside a service.
HTTP context accessor. Finally, you can use the IHttpContextAccessor helper service to get the HTTP context in any class that is managed by the ASP.NET Core dependency injection system. This is useful when you have a common service that is used by your controllers.
When an HTTP request arrives at the server, the server processes the request and builds an HttpContext object. This object represents the request which your application code can use to create the response.
Inject IHttpContextAccessor
in the constructor
By injecting an HttpContext
into your component you are violating the SOLID principles. To be more specifically, you are violating:
HttpContext
).HttpContext
has many methods, while the consumer never uses them all.Both violations make it much harder to test your code. Although you can instead inject the IHttpContextAccessor
as @victor suggests, this is still a violation of both the DIP and ISP, because this is an abstraction that is provided by the framework and you still depend on HttpContext. According to the DIP it is the client who should define the abstraction. This causes your code to be needlessly coupled to the framework.
Instead you should strive to specify narrow role interfaces; interfaces that do one specific thing for you that is specific to the needs of your application. Injecting a big dictionary with string values (as what HttpContext
is, is never very specific). From your question it's unclear what kind of data you need from our MyAppContext
, but I expect something like information of the currently logged in user. For this you can define a specific IUserContext
abstraction, for instance:
public interface IUserContext {
IPrincipal CurrentUser { get; }
}
An adapter that connects the application to the ASP.NET framework can be easily created for this abstraction:
sealed class AspNetUserContextAdapter : IUserContext {
private readonly IHttpContextAccessor accessor;
public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public IPrincipal CurrentUser => accessor.HttpContext.User;
}
This adapter does depend on IHttpContextAccessor
, but this is okay, since the adapter is an infrastructural component located in the Composition Root. There are serveral ways to register this class, for instance:
services.AddSingleton<IUserContext, AspNetUserContext>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With