Настройка HttpContext.Current с использованием HttpContextBase - PullRequest
0 голосов
/ 12 июня 2018

У меня есть длительное задание, которое я запускаю в потоке.Я хотел бы установить HttpContext.Current в этой теме, чтобы мой HttpContextFactory мог получить текущий HttpContext.

Вот мой класс TaskRunner:

public class TaskRunner
{
    public TaskRunner(
        IQueueProcessorFactory queueProcessorFactory,
        IHttpContextFactory httpContextFactory)
    {
        _queueProcessorFactory = queueProcessorFactory;
        _httpContextFactory = httpContextFactory;
    }

    public void StartQueueProcessorThread()
    {
        var currentContext = _httpContextFactory.Create(); // Simply Gets new HttpContextWrapper(HttpContext.Current);
        queueProcessor = new Thread(
        () =>
        {
            HttpContext.Current = currentContext; // Cannot implicitly convert type 'System.Web.HttpContextBase' to 'System.Web.HttpContext'
            _queueProcessorFactory.Create().ProcessQueue(); // Log running task
        })
        { Name = "QueueProcessor" };
        queueProcessor.Start();
    }
}

Существует ли простой способ установитьHttpContext.Current с использованием введенного _httpContextFactory?

1 Ответ

0 голосов
/ 13 июня 2018

То, что вы хотите, не возможно, и вы не должны пытаться достичь этого.Ваш вопрос вызван кодом, который жестко зависит от HttpContext.Current.Это нарушение принципа инверсии зависимости.Поскольку ваш код выполняется в фоновом потоке, он не выполняется в контексте HTTP-запроса и поэтому не должен использовать информацию запроса.

Решение состоит в том, чтобы определить абстракции для конкретного приложения, которые абстрагируют HttpContext.Например, если для этого кода требуется информация о текущем пользователе, определите абстракцию IUserContext:

public interface IUserContext
{
    Guid UserId { get; }
}

. Это позволит вам внедрить другую реализацию IUserContext при работе в фоновом потоке.

Реализация IUserContext для ваших веб-запросов может выглядеть следующим образом:

public sealed class AspNetUserContext : IUserContext
{
    public Guid UserId => (Guid)HttpContext.Current.Session["UserId"];
}

Пользовательский контекст, который вы используете в фоновом потоке, с другой стороны, может быть реализован следующим образом:

public sealed class ThreadStaticUserContext : IUserContext
{
    [ThreadStatic]
    public static Guid UserIdField;

    public Guid UserId => this.UserIdField;
}

Этот ThreadStaticUserContext позволяет установить UserId.Если вы хотите выделить фоновый поток, который запускается с тем же идентификатором пользователя, что и исходный запрос, вам придется передать идентификатор пользователя в фоновый поток и установить значение FixedUserContext.UserId перед выполнением полной операции.Это может выглядеть так:

public AsynchronousWelcomeMessageSenderDecorator : IWelcomeMessageSender
{
    private readonly IUserContext userContext;
    private readonly Func<IWelcomeMessageSender> senderFactory;

    public AsynchronousWelcomeMessageSenderDecorator(
        IUserContext userContext, 
        Func<IWelcomeMessageSender> senderFactory) { ... }

    public void SendWelcomeMessage(WelcomeMessage message)
    {
        // Read the user ID while running on the request thread
        Guid userId = this.userContext.UserId;

        ThreadPool.QueueUserWorkItem(_ =>
        {
            // Set the user ID as the first thing to do on the background thread.
            ThreadStaticUserContext.UserIdField = userId;

            // Resolve the 'real' sender within the thread.
            IWelcomeMessageSender realSender = this.senderFactory();

            // Forward the call to the real sender.
            realSender.SendWelcomeMessage(message);
        });
    }
}

Чтобы дать вам представление, без DI-контейнера эту часть графа объектов можно построить следующим образом:

IWelcomeMessageSender sender =
    new AsynchronousWelcomeMessageSenderDecorator(
        new AspNetUserContext(),
        () => new EmailMessageSender(new ThreadSpecificUserContext()));

Другими словамилюбой компонент, который зависит от IWelcomeMessageSender, будет введен AsynchronousWelcomeMessageSenderDecorator.Когда вызывается его SendWelcomeMessage, он раскручивает фоновый поток, который запросит IWelcomeMessageSender через senderFactory.senderFactory создаст новый EmailMessageSender, который будет отправлять фактическую почту.Это EmailMessageSender снова зависит от IUserContext, но в этом случае оно вводится с ThreadSpecificUserContext.

...