.Net Core Hosted Service требует HttpContext - PullRequest
0 голосов
/ 01 июля 2018

РЕДАКТИРОВАТЬ Резюме

У меня есть фоновая служба, для которой требуется DBContext, мой DbContext зависит от HttPContext, потому что он использует UserClaims для фильтрации контекста. HttpContext, который является нулевым при запросе DbContext от BackgroundService (это сделано из-за отсутствия WebRequest, связанного с BackgroundService).

Как я могу перехватить и записать HTTPContext, чтобы мои заявления пользователей перенесли на фоновую службу?

Я пишу размещенный сервис, чтобы помещать в очередь посылку сообщений из моих концентраторов в Signalr. Мой фон.

public interface IBackgroundTaskQueue
{
    void QueueBackgroundWorkItem(Func<IServiceProvider, CancellationToken, Task> workItem);
    Task<Func<IServiceProvider, CancellationToken, Task>> DequeueAsync(CancellationToken cancellationToken);
}

public class BackgroundTaskQueue : IBackgroundTaskQueue
{
    private ConcurrentQueue<Func<IServiceProvider, CancellationToken, Task>> _workItems = new ConcurrentQueue<Func<IServiceProvider, CancellationToken, Task>>();
    private SemaphoreSlim _signal = new SemaphoreSlim(0);

    public void QueueBackgroundWorkItem(Func<IServiceProvider, CancellationToken, Task> workItem)
    {
        if (workItem == null)
        {
            throw new ArgumentNullException(nameof(workItem));
        }

        this._workItems.Enqueue(workItem);
        this._signal.Release();
    }

    public async Task<Func<IServiceProvider, CancellationToken, Task>> DequeueAsync(
        CancellationToken cancellationToken)
    {
        await this._signal.WaitAsync(cancellationToken);
        this._workItems.TryDequeue(out var workItem);

        return workItem;
    }
}

Я работаю с очередями, которым требуется мой DbContext, My Db Context автоматически фильтрует данные на основе зарегистрированного пользователя, поэтому ему требуется доступ к HTTPContextAccessor.

Когда я ставлю в очередь элемент, для которого требуется мой DbContext, я получаю сообщение об ошибке, что HTTPContext имеет значение null, что имеет смысл, потому что я работаю в фоновом процессе.

var backgroundTask = sp.GetRequiredService<IBackgroundTaskQueue>();

backgroundTask.QueueBackgroundWorkItem((isp, ct) => {
    //When this line is executed on the background task it throws
    var context = sp.GetService<CustomDbContext>(); 
    //... Do work with context
});

Мой DBContext использует HTTPContextAccessor для фильтрации данных:

public CustomDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor)
{
}

Есть ли способ, которым я могу захватить HTTPContext с каждой задачей, или, возможно, Moq один и захватить UserClaims, и заменить их в области действия для BackgroundService?

Как я могу использовать службы, зависящие от HTTPContext, от моих фоновых служб.

Ответы [ 2 ]

0 голосов
/ 01 июля 2018

Мой DbContext зависит от HttpContext, потому что он использует UserClaims для фильтрации контекста.

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

Что касается насмешки над контекстом, вам не нужно издеваться над ним с помощью насмешливой библиотеки, такой как Moq. Вы можете просто создать DefaultHttpContext и установить внутри пользователя, например ::

var context = new DefaultHttpContext()
{
    User = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
    {
        new Claim(ClaimTypes.Name, "Foo"),
    })),
};

Но возможность создания контекста HTTP не поможет вам предоставить контекст для контекста вашей базы данных: вы не можете заменить зарегистрированные сервисы после того, как поставщик услуг был создан, поэтому вам придется либо установить контекст на HttpContextAccessor - который я бы настоятельно рекомендовал, так как это может быть небезопасно для использования внутри фоновой службы - или для явного создания контекста вашей базы данных, - что я бы тоже советовал, поскольку это нанесет ущерб целям DI.

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

Как правило, помните, что контексты базы данных являются зависимостями в рамках, поэтому вам все равно не следует разрешать их вне области зависимостей. Если вам нужен контекст базы данных в фоновом сервисе, вы должны явно открыть область зависимостей, используя IServiceScopeFactory.

0 голосов
/ 01 июля 2018

Вы не поделились своей конфигурацией, поэтому вам придется простить меня, как я собираюсь догадаться здесь ...

Возможно, вы получаете значение null для IHttpContextAccessor из-за некоторых проблем с внедрением зависимостей.

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

На самом деле, я верю, что вы также можете достичь того же, выполнив

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddHttpContextAccessor(); // does pretty much the same as above
}

источник для этого расширения здесь

...