Как получить экземпляр Microsoft.AspNet.Http.HttpContext в Class Constructor с использованием DI

Я создаю одноразовое приложение в MVC 6 и экспериментирую с разными архитектурами для зависимостей.

Проблема, с которой я сталкиваюсь, заключается в том, как создать пользовательский объект "MyAppContext", специфичный для приложения. Для этого потребуется некоторая информация из HttpContext и некоторая информация из базы данных и будет репозиторием с запросом для атрибутов, специфичных для приложения. Я хочу передать экземпляр HttpContext в конструктор "MyAppContext".

Я успешно создал объект DataService с интерфейсом IDataService, используя DI, и это работает нормально. Разница с классом "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: не удается разрешить службу для типа "Microsoft.AspNet.Http.HttpContext" при попытке активировать "MyAppContext"

Я полагаю, это связано с тем, что не существует конкретного экземпляра HttpContext, что эта ошибка возникает, но я не знаю, как зарегистрировать экземпляр HttpContext в DI. Я добавил строку 'app.UseRequestServices();', но это не имело никакого значения. Я также попробовал вариант:

services.AddScoped<HttpContext, HttpContext>();

Но это терпит неудачу, потому что второй HttpContext должен быть экземпляром - я знаю, что он не правильный, но не смог определить, что есть.

Итак, в резюме - как передать объект HttpContext в конструктор MyAppContext?

Ответ 1

Inject IHttpContextAccessor в конструкторе

Ответ 2

Вводя HttpContext в ваш компонент, вы нарушаете принципы SOLID. Чтобы быть более конкретным, вы нарушаете:

Оба нарушения затрудняют тестирование кода. Хотя вы можете вместо этого ввести 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, но это нормально, поскольку адаптер представляет собой инфраструктурный компонент, расположенный в Root of Composition. Существуют серверные способы регистрации этого класса, например:

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

Ответ 4

Зачем вам передавать HttpContext в конструкторе? Почему бы не получить к нему доступ туда, где хотите?

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