如何使用DI在Class Constructor中获取Microsoft.AspNet.Http.HttpContext实例

时间:2015-05-05 14:05:22

标签: c# asp.net-mvc dependency-injection asp.net-core asp.net-core-mvc

我正在MVC 6中构建一次性应用程序,并尝试使用不同的依赖架构。

我面临的问题是如何创建特定于应用程序的自定义“MyAppContext”对象。这将需要来自HttpContext的一些信息和来自数据库的一些信息,并且将是针对特定于应用程序的属性的请求范围的存储库。我想将HttpContext的实例传递给“MyAppContext”的构造函数。

我已经使用DI成功创建了一个带有DataService界面的“IDataService”对象,这样就可以了。 与'MyAppContext'类的不同之处在于它在构造函数中有两个参数 - “DataService”和Microsoft.AspNet.Http.HttpContext。这是MyAppContext类:

public class MyAppContext : IMyAppContext
{
    public MyAppContext(IDataService dataService, HttpContext httpContext)
    {
       //do stuff here with the httpContext
    }
}

在启动代码中,我注册了DataService实例和MyAppContext实例:

    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 */);
    }

我希望构造函数中的HttpContext参数能够被DI解析。 运行代码时,这是我返回的异常:

  

InvalidOperationException:尝试激活“MyAppContext”时无法解析类型“Microsoft.AspNet.Http.HttpContext”的服务

我认为这是因为没有HttpContext的特定实例发生此错误,但我不知道如何在DI中注册HttpContext实例。我添加了“app.UseRequestServices();”这一行,但这没有任何区别。我也试过了一个变体:

services.AddScoped<HttpContext, HttpContext>();

但这失败了,因为第二个HttpContext应该是一个实例 - 我知道它不正确但是无法弄清楚是什么。

总而言之 - 如何将HttpContext对象传递给MyAppContext的构造函数?

4 个答案:

答案 0 :(得分:17)

在构造函数中注入IHttpContextAccessor

答案 1 :(得分:15)

通过向您的组件中注入HttpContext,您违反了SOLID principles。更具体地说,你违反了:

这两种违规都会使测试代码变得更加困难。虽然您可以将IHttpContextAccessor注入@victor建议,但这仍然违反了DIP和ISP,因为这是框架提供的抽象,您仍然依赖于HttpContext。根据DIP,客户应该定义抽象。这会导致您的代码不必要地耦合到框架。

相反,你应该努力指定狭窄的角色接口;为您执行特定于您的应用程序需求的特定事物的接口。注入一个包含字符串值的大字典(与HttpContext一样,从来都不是非常具体)。根据您的问题,我们不清楚您需要从MyAppContext获得哪种数据,但我希望有类似当前登录用户的信息。为此,您可以定义特定的IUserContext抽象,例如:

public interface IUserContext {
    IPrincipal CurrentUser { get; }
}

可以轻松地为此抽象创建将应用程序连接到ASP.NET框架的适配器:

sealed class AspNetUserContextAdapter : IUserContext  {
    private readonly IHttpContextAccessor accessor;
    public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
        this.accessor = accessor;
    }
    public IPrincipal CurrentUser => accessor.HttpContext.User;
}

此适配器确实依赖IHttpContextAccessor,但这没关系,因为适配器是位于Composition Root的基础结构组件。有几种方法可以注册这个类,例如:

services.AddSingleton<IUserContext, AspNetUserContext>();

答案 2 :(得分:3)

在启动课程中:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.Extensions.DependencyInjection;

public void ConfigureServices(IServiceCollection services)
{        
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddMvcCore();
}

在控制器中:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;

private readonly IHttpContextAccessor _httpContextAccessor;

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

答案 3 :(得分:-5)

为什么要在构造函数中传递HttpContext? 为什么不在任何地方直接访问它?

public MyAppContext(IDataService dataService)
    {
       HttpContext mycontext = HttpContext.Current;
       //do stuff here with mycontext
    }