我正在尝试使用ASP.NET Core的内置DI容器将一个 singleton 服务注入到一个actor(Akka.NET)中。
我在ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
// ..
// Register singleton of service
services.AddSingleton<IMyService, MyService>();
// Build service provider
var provider = services.BuildServiceProvider();
// Create actor system
var system = ActorSystem.Create("MyActorSystem");
// Inject service singleton into actor
directory.MyActorRef
= system.ActorOf(MyActor.Props(provider.GetService<IMyService>()), "myactor");
}
问题在于,actor中MyService的实例与注入应用程序其余部分的实例不同 - 即它不是单例。
我做错了什么,有更好的方法吗?
答案 0 :(得分:2)
这是因为您在ConfigureServices
// Build service provider
var provider = services.BuildServiceProvider();
此行将创建一个新的服务提供者(IoC容器)。当您从中解析服务时,它们实际上是单例(因为它没有从作用域提供程序中解析)。
您不应该在.BuildServiceProvider()
方法中调用ConfigureServices
,除非使用第三方容器并创建它(即使用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
内解决问题并不是很干净,你应该使用docs(或问一个单独的问题)如何正确使用DI与Akka.NET (抱歉不熟悉它,我是微软奥尔良用户:)。)。
稍微好一些(仍然不完全正确,因为它适用于DI的想法)方式是延迟actor的实例化,直到调用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
初始化并解析您的服务。
P.S。请记住,您无法解决来自app.ApplicationServices
或服务提供商的范围服务,它会引发异常。当您想要使用DbContext时,这可能会成为一个问题,默认情况下,DbContext被注册为作用域服务。
您也可以将其注册为覆盖范围,覆盖AddDbContext
,但要注意&#34;内存泄漏&#34;,随着跟踪对象数量的增加,内存消耗也会增加(并且被跟踪实体的数量(&gt; = 10k)将显着减少与跟踪器相关的操作)。
考虑到DbContext,还要记住EF和EF Core不是线程安全的,并且不能被线程访问(或者运行多个异步操作,即启动5个查询,无需等待和然后使用await Task.WaitAll(...)
)。
虽然保证演员一次只能被一个线程访问,但是如果你考虑范围,则服务不是。
这有多好用取决于Akka.NET使用的Task Scheduler实现(再次,不熟悉它的内部结构 - 即Orleans抽象存储提供商后面的持久性)。