无法将 IServiceProvider 直接解析为基础 class ASP.NET 核心。我不想通过任何 child class 的构造函数传递它

Trouble in resolving IServiceProvider directly into the base class ASP.NET Core. I don't want to pass it through constructors of any child class

我有一棵 classes 树,其中一个继承了另一个。所有这些基础 classes 都有不同类型的功能。所以继承 class 可以选择去哪一个。功能从基础 class 向上移动。这些 classes 的基本功能需要 IServiceProvider 才能解决。

我不喜欢的第一个解决方案是将 IServiceProvider 传递给处理程序 class,它从一个碱基移动到下一个碱基,依此类推。

第二个是我要找的。我正在寻找一种解决方法,我应该能够创建不带参数的处理程序 classes 的实例,并且基础 class 本身应该在需要时解析 IServiceProvider

直到现在我都找不到解决办法。

使问题有意义的代码示例我正在添加一些代码框架。

abstract class Root<ResultType, DbClass>
    where ResultType : class
    where DbClass : class
{
    private DbContext ent;
    abstract ResultType DefineHowToCreateResult(DbClass inputClass);

    IEnumerable<ResultType> GetAll()
    {
        // This class itself should be able to resolve the context. So everytime
        // it should not expect it to come all the way from child
        ent = ResolveContext<DbContext>();
        return DefineHowToCreateResult(ent.DbClass());
    }

    DbClass AddNew(DbClass inputPara)
    {
        // This class itself should be able to resolve the context. So everytime
        // it should not expect it to come all the way from child
        ent = ResolveContext<DbContext>();
        ent.AddNew(inputPara);
        return DbClass;
    }
}

一些功能属于这个中间体的范围 class。但它永远不应该执行 GetAll()AddNew() 类功能。因为它应该由 Root class.

处理
abstract class IntermediatRoot<ResultType, DbClass, InterMediatPara>
    : Root<ResultType, DbClass>
    where ResultType : class
    where DbClass : class
    where InterMediatPara : class
{
    ResultType PerformInterMediatWork()
    {
        var allData = GetAll();
        //------
        //------
        //------
        //------
        //------
        return ResultType;
    }
}

我只是希望它是一个无参数的构造函数,不想一路走下去IServiceProvider。因为最终它在 Root class 上是必需的,我希望 root class 能够解析执行我想要的。这将使 class 有点独立,并且 self-responsible 对于给定的工作范围。

internal class HandlerClass : IntermediatRoot<H1, H2, H3>
{
    private override H1 DefineHowToCreateResult(H2 inputClass)
    {
        //------
        //------
        //------
        //------
    }
}
class H1 { }
class H2 { }
class H3 { }

在进一步说明之前,我必须先说这一点...在层次结构中沿 child class 传递 IServiceProvider 有什么问题? 如果 child class 不使用它,那么就减少它通过,否则,创建一个 private readonly 属性 应该没问题。因为 DI 会自动提供正确的范围,IServiceProvider 你应该使用。

如果你非常想要它......创建一个包含服务提供商的实例,如

public class ServiceProviderContainer : IDisposable
    {
        private static IHttpContextAccessor _httpContextAccessor;
        private static readonly ConcurrentDictionary<string, IServiceProvider> ServiceProviderContainers = new();

        public ServiceProviderContainer(IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
            if (!string.IsNullOrEmpty(httpContextAccessor.HttpContext?.TraceIdentifier))
            {
                ServiceProviderContainers.TryAdd(httpContextAccessor.HttpContext?.TraceIdentifier, serviceProvider);
            }
        }

        public static IServiceProvider ResolveCommonServiceProvider() =>
            _httpContextAccessor.HttpContext?.TraceIdentifier != null &&
            ServiceProviderContainers.TryGetValue(_httpContextAccessor.HttpContext.TraceIdentifier,
                out var serviceProvider)
                ? serviceProvider
                : throw new InvalidOperationException(
                    $"failed to get the Service Provider. Current trace Id: {_httpContextAccessor.HttpContext?.TraceIdentifier}");

        public void Dispose()
        {
            if (!string.IsNullOrEmpty(_httpContextAccessor.HttpContext?.TraceIdentifier))
            {
                ServiceProviderContainers.TryRemove(_httpContextAccessor.HttpContext.TraceIdentifier, out _);
            }
        }
    }

将其注册为作用域 services.AddScoped<ServiceProviderContainer>(); - 这是您唯一想要注册服务提供商之类的生命周期!

如果您已经在执行逻辑之上设置了一个中间件,请将其放在那里以确保在每次 http 请求时都创建它:

public class SomeUpperMiddleware
    {
        public SomeUpperMiddleware(ServiceProviderContainer container)
        {
        }
    }

那么现在,您可以在应用程序的任何地方随意使用它:

var container = ServiceProviderContainer.ResolveCommonServiceProvider();
var logger = container.GetRequiredService<ILogger<ProductController>>();

但是让我再次强调,应该避免这种方法,因为它会使 IServiceProvider 的范围变暗。