Невозможно воспроизвести в тестах исключение Невозможно использовать сервис с областью действия из синглтона вне Mvc фреймворка - PullRequest
1 голос
/ 01 апреля 2020

InvalidOperationException Cannot consume scoped service from singleton - это хорошо известный сценарий , который очень хорошо описан здесь

Я исследую способ воспроизведения этого исключения (предполагая, что оно исходит из структуры внедрения зависимостей), но я не удается.

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

public class Given_Scoped_Repository_And_Singleton_Service_That_Uses_The_Repository_When_Getting_Service_From_Different_Scope_After_Disposing_First_Scope
    : Given_When_Then_Test
{
    private IServiceScope _scopeOne;
    private IServiceScope _scopeTwo;
    private ServiceSample _serviceSampleOne;
    private ServiceSample _serviceSampleTwo;

    protected override void Given()
    {
        var serviceCollection =
            new ServiceCollection()
                .AddScoped<RepositorySample>()
                .AddSingleton<ServiceSample>()
                .BuildServiceProvider();

        _scopeOne = serviceCollection.CreateScope();
        _scopeTwo = serviceCollection.CreateScope();

        _serviceSampleOne = _scopeOne.ServiceProvider.GetService<ServiceSample>();

        _scopeOne.Dispose();
    }

    protected override void When()
    {
        _serviceSampleTwo = _scopeTwo.ServiceProvider.GetService<ServiceSample>();
    }

    [Fact]
    public void Then_It_Should_Get_The_Same_Service_Instance()
    {
        _serviceSampleOne.Should().Be(_serviceSampleTwo);
    }

    [Fact]
    public void Then_It_Should_Have_The_Same_Repository_Instance()
    {
        _serviceSampleOne.RepositorySample.Should().Be(_serviceSampleTwo.RepositorySample);
    }
}

и

class RepositorySample { }
class ServiceSample
{
    public RepositorySample RepositorySample { get; }

    public ServiceSample(RepositorySample repositorySample)
    {
        RepositorySample = repositorySample;
    }
}

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

  • Почему структура внедрения зависимостей Microsoft.Extensions.DependencyInjection 3.1.3 не генерирует ли исключение, предупреждающее меня о ловушке одноэлементной области?
  • Если я выберу первую область (как если бы это был DbContext с областью действия в сценарии Mvc, который должен d ie, когда ответ выдается), не должно ли это привести к удалению экземпляра RepositorySample? Это не так, даже когда у меня есть RepositorySample реализовать IDisposable, я вижу, что экземпляр с областью действия никогда не удаляется (метод Dispose не выполняется)
  • Как я могу создать свой тест, чтобы увидеть исключение как если это происходило с DbContext с областью действия, который создается в одноэлементном сервисе?

1 Ответ

1 голос
/ 02 апреля 2020

Это исключение не выдается, поскольку не была указана опция проверки областей действия.

Измените это:

.BuildServiceProvider();

на следующее:

.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });

ServiceProviderOptions.ValidateScopes

Значение true, чтобы выполнить проверку, подтверждающую, что услуги с областью действия никогда не будут получены от поставщика root;

Вы также можете выполнить проверку когда ServiceProvider создается вместо ожидания разрешения службы:

.BuildServiceProvider(
    new ServiceProviderOptions
    {
        ValidateScopes = true,
        ValidateOnBuild = true
    });

Это то, что приложение MVC делает при запуске. Вот почему исключение выдается при запуске, а не при разрешении контроллера. Он выдаст это исключение, если обнаружит ошибки, даже если зарегистрированные зависимости никогда не будут разрешены во время выполнения. Чтобы увидеть это, зарегистрируйте свои зависимости - ServiceSample, RepositorySample - как вы делали в модульном тесте, но не вставляйте их ни в какие контроллеры. Вы все равно получите исключение при запуске, когда попытаетесь собрать ServiceProvider.

...