Область действия не соблюдается с помощью локатора службы в .NET Core

Части моего кода должны использовать ServiceLocator, потому что внедрение конструктора не поддерживается.

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

Например:

services.AddScoped<IAppSession, AppSession>();
services.AddScoped<IAuthentication, Authentication>();
services.AddScoped<NotificationActionFilter>();

В конце моих определений службы у меня есть следующий блок кода, который настраивает локатор службы.

var serviceProvider = services.BuildServiceProvider();
DependencyResolver.Current = new DependencyResolver();
DependencyResolver.Current.ResolverFunc = (type) =>
{
    return serviceProvider.GetService(type);
};

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

Код для DependencyResolver выглядит следующим образом:

public class DependencyResolver
{
    public static DependencyResolver Current { get; set; }

    public Func<Type, object> ResolverFunc { get; set; }

    public T GetService<T>()
    {
        return (T)ResolverFunc(typeof(T));
    }
}

Как я могу это исправить?


person Sam    schedule 28.11.2018    source источник
comment
Вы не воссоздаете область действия, поэтому службы с ограниченной областью действия не учитывают область действия. Также у вас есть 2 поставщика услуг - это объясняет, почему вы получаете разные экземпляры.   -  person Alex Riabov    schedule 28.11.2018
comment
Не могли бы вы предоставить реализацию для DependencyResolver?   -  person Alex Riabov    schedule 28.11.2018
comment
@AlexRiabov Сейчас я добавлю полную реализацию.   -  person Sam    schedule 28.11.2018


Ответы (1)


Я бы предложил создать промежуточное программное обеспечение, которое установит ServiceProvider на тот, который используется в других местах:

public class DependencyResolverMiddleware
{
    private readonly RequestDelegate _next;

    public DependencyResolverMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        DependencyResolver.Current.ResolverFunc = (type) =>
        {
            return httpContext.RequestServices.GetService(type);
        };

        await _next(httpContext);
    }
}

Кроме того, DependencyResolver следует обновить для поддержки такого поведения:

public class DependencyResolver
{
    private static readonly AsyncLocal<Func<Type, object>> _resolverFunc = new AsyncLocal<Func<Type, object>>();

    public static DependencyResolver Current { get; set; }

    public Func<Type, object> ResolverFunc
    {
        get => _resolverFunc.Value;
        set => _resolverFunc.Value = value;
    }

    public T GetService<T>()
    {
        return (T)ResolverFunc(typeof(T));
    }
}

Не забудьте прописать его в методе Configure в Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    app.UseMiddleware<DependencyResolverMiddleware>();
}
person Alex Riabov    schedule 28.11.2018
comment
Это не сработает. Существует состояние гонки при настройке функции резолвера с одновременными запросами. - person Sam; 28.11.2018
comment
@ Сэм, ты прав, я обновил ответ, так что это больше не должно быть проблемой. - person Alex Riabov; 28.11.2018
comment
Отличная работа, спасибо. Также - не слышал об AsyncLocal, приятно узнать что-то новое. - person Sam; 28.11.2018