如何在ASP.NET Core中注册单例工厂以解析作用域对象?

3
当我像这样为IPersonProvider注册工厂时:
services.AddScoped<IPersonProvider, PersonProvider>();
services.AddSingleton<Func<IPersonProvider>>(sp => () => sp.GetService<IPersonProvider>());

我遇到了错误:

InvalidOperationException: 无法从根提供程序解析范围服务 'DependencyApp.Provider.IPersonProvider'。

我知道不能使用Scoped依赖项来解析Singleton对象,但据我所知,我的工厂没有任何Scoped依赖项 - 实际上它没有任何依赖项。当我像这样请求时,它只是创建对象:

public class HomeController : Controller
{
    private readonly Func<IPersonProvider> _factory;

    public HomeController(Func<IPersonProvider> factory)
    {
        _factory = factory;
    }

    [Route("/")]
    public string Calculate()
    {
        var personProvider = _factory();
        //.
        //.
        //.

        return "test";
    }
}

为什么会出现这个错误?如何解决问题?我认为将工厂注册为单例,可以像整个应用程序一样长时间存活,不应该有任何问题创建范围对象 - 最终那些创建的对象并不是单例的依赖项。

如果将您的Func设置为瞬态服务,问题是否仍然存在?问题可能在于,由于它是单例模式,因此sp实例始终是根实例,但如果将服务定义为瞬态,则情况可能并非如此。 - Matthew
它可以工作,当我将Func Singleton保持不变但将IPersonProvider设置为瞬态时,它也可以工作。但是我想知道为什么不能将Func Singleton和provider Scoped一起使用?Func只是创建对象,所以我认为应该每个应用程序只创建一次。 - CSharpBeginner
2个回答

6
您遇到此问题的原因是由于使用了 IServiceProvider 的实例。 当您将服务注册为单例时,根 IServiceProvider 将传递到工厂方法中。
因此,当您有以下代码时:
services.AddSingleton<Func<IPersonProvider>>(sp => () => sp.GetService<IPersonProvider>());

这意味着sp必须与单例生命周期兼容,这仅适用于根服务提供程序。

为了说明我的意思。

using (var scopedServiceProvider = sp.CreateScope())
{
    scopedServiceProvider.GetService<IPersonProvider>(); // valid

    sp.GetService<IPersonProvider>(); // not valid
}

然而,当您的工厂方法是短暂生命周期时,传递的 sp 不再使用根服务提供程序,而是使用派生服务提供程序,其中不应用实例化范围服务的限制。
重申问题,您正在使用的 sp 不能创建作用域服务,因此问题不在于返回的服务是一个 Func,而在于提供程序是根提供程序。

-1

我已经按照要求完成了,一切都正常:

 services.AddSingleton<ITokenManager, TokenManger>();
 services.AddScoped<ITokenProvider, TokenProvider>();

public class TokenProvider : ITokenProvider
{
    private readonly ITokenManager _tokenManager;
    public TokenProvider(ITokenManager tokenManager)
    {
        _tokenManager = tokenManager;
    }
}

1
我认为 OP 想要相反的结果。 - Matthew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接