c# - 无法在测试中重现异常无法从 Mvc 框架外的单例中使用范围服务
问题描述
这InvalidOperationException Cannot consume scoped service from singleton
是一个众所周知的场景,这里描述得很好
我正在研究一种重现此异常的方法(假设它来自依赖注入框架),但我没有成功。
我创建了一个带有提交的 repo 来说明它,但基本上我有以下测试:
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
不抛出异常提醒我单例范围陷阱? - 如果我处置第一个作用域(就好像它是 Mvc 场景中的作用域 DbContext 在发出响应时应该死掉一样),这不应该导致
RepositorySample
实例被处置吗?不是,即使我已经RepositorySample
实现,IDisposable
我也可以看到范围内的实例永远不会被释放(Dispose 方法没有被执行) - 我如何设计我的测试来查看异常,就好像它发生在单例服务中实例化的作用域 DbContext 中一样?
解决方案
它没有抛出异常,因为尚未指定验证范围的选项。
改变这个:
.BuildServiceProvider();
对此:
.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
ServiceProviderOptions.ValidateScopes
true 执行检查以验证范围内的服务永远不会从根提供者处得到解析;
您还可以在ServiceProvider
构建时执行验证,而不是等到服务解决:
.BuildServiceProvider(
new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = true
});
这就是 MVC 应用程序在启动时所做的事情。这就是为什么在启动时抛出异常,而不是在解析控制器时抛出异常。如果它检测到错误,即使注册的依赖项在运行时永远不会被解析,它也会抛出该异常。要查看它,请注册您的依赖项 - ServiceSample
, RepositorySample
- 就像您在单元测试中所做的那样,但不要将它们注入任何控制器。尝试构建ServiceProvider
.