首页 > 解决方案 > 无法在存储库构造函数 asp.net core 中创建服务范围

问题描述

我有临时存储库服务,每次调用它时都需要创建服务范围。

我试图在存储库构造函数中创建这个范围,如下所示:

public class ServiceRepository : IServiceRepository
{
    private IServiceScopeFactory _serviceScopeFactory;
    private IServiceScope _scope;
    private IServiceProvider _serviceContainer;

    private DataBaseContext _db;

    public ServiceRepository(DataBaseContext context, IServiceScopeFactory serviceScopeFactory)
    {
        _db = context;
        _serviceScopeFactory = serviceScopeFactory;
        _scope = _serviceScopeFactory.CreateScope();
        _serviceContainer = _scope.ServiceProvider;
    }

之后我尝试从服务提供商调用我的存储库服务:

var serviceRepository = _serviceProvider.GetRequiredService<IServiceRepository>();

我希望每次以这种方式调用此服务时,都会创建我在存储库构造函数中声明的服务范围。但是在访问服务时,我得到了错误:

System.InvalidOperationException: 'Cannot resolve 'Data_Access_Layer.Interfaces.IServiceRepository' from root provider because it requires scoped service 'Data_Access_Layer.EF.DataBaseContext'.'

我究竟做错了什么?之前,我已经设置了这样的范围并且它有效:

var scopeFactory = _serviceProvider.GetService<IServiceScopeFactory>();
var scope = scopeFactory.CreateScope();
var scopedContainer = scope.ServiceProvider;

但在这种情况下,我需要在每次调用 IServiceRepository 之前声明范围。这就是为什么我想在 IServiceRepository 构造函数中声明范围。

标签: c#asp.net-coredependency-injectionrepository-pattern

解决方案


你用错了所有这些。首先,瞬态生命周期对象可以直接注入作用域服务。您不应该注入orIServiceProviderIServiceScopeFactory​​,而是注入您的实际依赖项。您已经直接注入了您的上下文(这是一个范围服务),所以我不确定您为什么要尝试以不同的方式处理任何其他事情。

只有当您的对象具有单例生命周期并且需要范围服务时,您才应该注入IServiceProvider(仅此而已)。这被称为服务定位器反模式,它之所以成为反模式是有原因的:您应该尽可能避免这样做。一般来说,大多数人认为应该是单例的东西实际上不应该是单例。只有少数情况下您真正需要单例生命周期。在所有其他情况下,“作用域”应该是您的首选生命周期。此外,如果您的单例确实需要范围服务,那么它实际上应该是范围本身的一个强有力的论据。

但是,如果您确实发现自己确实需要单例生命周期并且仍然需要范围服务,那么正确的方法如下:

public class MySingletonService
{
    private readonly IServiceProvider _provider;

    public MySingletonService(IServiceProvider provider)
    {
        _provider = provider;
    }

    ...
}

就是这样。您不会在构造函数内创建范围。从范围检索到的任何服务都只存在于该范围内,当范围消失时,服务也将消失。因此,您不能将作用域服务持久化到单例上的 ivar。相反,在需要此类服务的每个单独方法中,您需要执行以下操作:

using (var scope = _provider.CreateScope())
{
    var myScopedService = scope.ServiceProvider.GetRequiredService<MyScopedService>();
    // do something with scoped service
}

这也是服务定位器是反模式的另一个原因:它会导致大量迟钝和重复的代码。有时你别无选择,但大多数时候你会这样做。


推荐阅读