如何使用 DI 在类构造函数中获取 Microsoft.AspNet.Http.HttpContext 实例

问题描述 投票:0回答:4

我正在 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 的构造函数中?

c# asp.net-mvc dependency-injection asp.net-core asp.net-core-mvc
4个回答
23
投票

在构造函数中注入

IHttpContextAccessor


20
投票

通过将

HttpContext
注入到您的组件中,您违反了 SOLID 原则。更具体地说,您违反了:

  • 依赖倒置原则(DIP),因为您依赖于框架类型(
    HttpContext
    )。
  • 接口隔离原则(ISP),因为
    HttpContext
    有很多方法,而消费者永远不会使用它们。

这两种违规行为都会使测试代码变得更加困难。尽管您可以按照 @victor 的建议注入

IHttpContextAccessor
,但这仍然违反了 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>();

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;
}

-6
投票

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

public MyAppContext(IDataService dataService)
    {
       HttpContext mycontext = HttpContext.Current;
       //do stuff here with mycontext
    }
© www.soinside.com 2019 - 2024. All rights reserved.