在 ASP.NET core 中,我有一个单例服务,它使用范围服务,它本身需要实例化新服务本身。
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<MySingleton>();
services.AddScoped<MyScoped>();
}
}
public class MySingleton
{
private IServiceScopeFactory _serviceScopeFactory;
public MySingleton(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public async Task Foo()
{
AsyncServiceScope serviceScope = _serviceScopeFactory.CreateAsyncScope();
await serviceScope.ServiceProvider.GetService<MyScoped>().Bar();
await serviceScope.DisposeAsync();
}
}
public class MyScoped
{
private IServiceScopeFactory _serviceScopeFactory;
public MyScoped(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public async Task Bar()
{
AsyncServiceScope serviceScope = _serviceScopeFactory.CreateAsyncScope();
SomeType s = await (SomeType)ActivatorUtilities.CreateInstance(serviceScope .ServiceProvider, someType);
s.DoSomething();
await serviceScope.DisposeAsync();
}
}
一段时间后,当调用
await serviceScope.ServiceProvider.GetService<MyScoped>().Bar();
时,我收到错误:
您无法访问已处置的对象。 IServiceProvider!.
为什么我会收到此错误?解决此错误的最佳方法是什么? 我已经测试过注入
IServiceProvider
、IServiceProviderFactory
和 IServiceScopeFactory
。
根据我的理解,
IServiceScopeFactory
应该阻止访问已经处置的ServiceProvider。
简化你的例子,检查我是否理解。
假设您想使用单例,因为(比如说)有大量数据被初始化一次。然后剩下的逻辑需要划分范围。
因此,您创建了一个试图采用作用域依赖项的单例:
public interface IBarScoped
{
void Bar();
}
public interface IFooSingleton
{
void Foo();
}
public class FooSingleton
{
private readonly IDictionary<string, string> _singletonHugeAmountOfData;
private readonly IBarScoped _scoped;
public FooSingleton(IBarScoped scoped)
{
// 1. Great, we can set up our singleton data.
_singletonHugeAmountOfData = CreateLotsOfData();
// 2. But... This doesn't work.
// When the Scoped dependency changes, how will your DI re-generate your singleton?
// Answer is: it can't.
}
}
这没有道理。您是在要求每次作用域依赖项发生变化时都会重新构建您的单例吗?一旦执行完成,范围内的所有内容都会被处理。
但是你不希望整个事情都被限定范围,因为你的字典初始化很大。
所以相反:翻转你的想法,让你的单例成为大部分作用域,对于任何依赖于其他作用域依赖项的东西。然后将真正独立部分提取到不同的单例中,这不依赖于任何范围。
Foo
不再是单例。现在已确定范围。一切正常。
public interface IBarScoped
{
void Bar();
}
public interface IFooScoped
{
void Foo();
}
public interface IDataSingleton
{
IDictionary<string, string> SingletonHugeData();
}
public class FooScoped
{
private readonly IDataSingleton _singletonData;
private readonly IBarScoped _scoped;
public FooScoped(IDataSingleton singletonData, IBarScoped scoped)
{
// 1. Great, we can set up our singleton data.
_singletonData = singletonData;
// 2. And also, Foo can now be declared as scoped,
// which means we can take an IBarScoped dep as well.
_scoped = scoped;
}
}