那是因为您在 ConfigureServices 中创建了一个单独的 IoC 容器
// Build service provider
var provider = services.BuildServiceProvider();
这一行将创建一个新的服务提供者(IoC 容器)。当您从中解析服务时,它们实际上是单例(因为它不是从范围提供者中解析的)。
你永远不应该在你的ConfigureServices 方法中调用.BuildServiceProvider(),除非在使用 3rd 方容器并创建它时(即使用 Autofac 时)。
无论如何,如果您出于某种原因必须在 ConfigureServices 内创建提供程序,您需要将 ConfigureServices 的签名更改为
// Return value from void to IServiceProvider
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var provider = services.BuildServiceProvider();
// don't call services.AddXxx(..) after this point! The container is already created and its registrations can't be changed
...
return provider;
}
这将使 ASP.NET Core 使用此容器,而不是创建自己的容器并将其传递给 Configure 方法。
虽然这可能会立即解决您的问题,但在 ConfigureServices 内部进行这种解析并不是很干净,您应该使用文档(或提出单独的问题)了解如何正确使用 DI 与 Akka.NET(对不起不熟悉,我是 Microsoft Orleans 用户 :))。
一个稍微好一点(仍然不完全正确,因为它围绕 DI 的想法工作)的方法是延迟演员的实例化,直到调用 Configure 方法。
public void ConfigureServices(IServiceCollection services)
{
// ..
// Register singleton of service
services.AddSingleton<IMyService, MyService>();
}
public void Configure(IApplicationBuilder app)
{
// Create actor system
var system = ActorSystem.Create("MyActorSystem");
// Inject service singleton into actor
directory.MyActorRef
= system.ActorOf(MyActor.Props(app.ApplicationServices.GetService<IMyService>()), "myactor");
}
或
public void ConfigureServices(IServiceCollection services)
{
// ..
// Register singleton of service
services.AddSingleton<IMyService, MyService>();
}
// inject it in Configure
public void Configure(IApplicationBuilder app, IMyService myService)
{
// Create actor system
var system = ActorSystem.Create("MyActorSystem");
// Inject service singleton into actor
directory.MyActorRef
= system.ActorOf(MyActor.Props(myService), "myactor");
}
这将初始化并解析您在Configure 中的服务。
关于单例、作用域和参与者的备注
附:请记住,您无法从app.ApplicationServices 或服务提供商解析范围服务,它会抛出异常。当您想要使用默认注册为作用域服务的 DbContext 时,这可能会成为一个问题。
您也可以将其注册为具有覆盖AddDbContext 的作用域,但请注意“内存泄漏”,随着跟踪对象数量的增加,内存消耗(以及大量跟踪实体 (>= 10k)将显着减少与跟踪器相关的操作)。
考虑到 DbContext,还要记住 EF 和 EF Core 不是线程安全的,并且不能被线程访问(或运行多个异步操作,即开始 5 个查询而不等待,然后使用await Task.WaitAll(...))。
虽然一个演员被保证一次只能被一个线程访问,但如果你确定它们的范围,服务就不会。
这工作的好坏取决于 Akka.NET 使用的任务调度器实现(同样,不熟悉它的内部 - 即 Orleans 抽象存储提供程序背后的持久性)。