Asp.net core 将服务单例注入ASP.NET核心中的actor(Akka.NET)中

Asp.net core 将服务单例注入ASP.NET核心中的actor(Akka.NET)中,asp.net-core,asp.net-core-2.0,akka.net,Asp.net Core,Asp.net Core 2.0,Akka.net,我正在尝试使用ASP.NET内核的内置DI容器将服务的单例注入到actor(Akka.NET)中 我已在ConfigureServices中执行了以下操作: public void ConfigureServices(IServiceCollection services) { // .. // Register singleton of service services.AddSingleton<IMyService, MyService>

我正在尝试使用ASP.NET内核的内置DI容器将服务的单例注入到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");
}
public void配置服务(IServiceCollection服务)
{
// ..        
//注册服务单例
services.AddSingleton();
//构建服务提供商
var provider=services.BuildServiceProvider();
//创建参与者系统
var system=ActorSystem.Create(“MyActorSystem”);
//将服务单例注入到actor中
directory.MyActorRef
=system.ActorOf(MyActor.Props(provider.GetService()),“MyActor”);
}
问题在于actor中的MyService实例与注入到应用程序其余部分的实例不同,即它不是单例


我做错了什么?还有更好的方法吗?

这是因为您在
配置服务中创建了一个单独的IoC容器

//生成服务提供程序
var provider=services.BuildServiceProvider();
此行将创建一个新的服务提供商(IoC容器)。当您从它解析服务时,它们实际上是单例的(因为它不是从作用域提供程序解析的)

您不应该在
ConfigureServices
方法中调用
.BuildServiceProvider()
,除非使用第三方容器并创建它(即使用Autofac)

无论如何,如果出于某种原因必须在
ConfigureServices
中创建提供者,则需要将
ConfigureServices
的签名更改为

//将值从void返回到IServiceProvider
公共IServiceProvider配置服务(IServiceCollection服务)
{
var provider=services.BuildServiceProvider();
//在此之后不要调用services.AddXxx(..)!容器已创建,其注册无法更改
...
退货供应商;
}
这将使ASP.NET Core使用此容器,而不是创建自己的容器并将其传递给
Configure
方法

虽然这可能会解决您眼前的问题,但在
ConfigureServices
内部进行此类解决并不十分干净,您应该使用文档(或单独提问)了解如何正确使用Akka.NET中的DI(很抱歉,我不熟悉它,我是Microsoft Orleans用户:)

一个稍微好一点的方法(仍然不是完全正确的,因为它围绕DI的思想工作)是延迟actor的实例化,直到调用
Configure
方法

public void配置服务(IServiceCollection服务)
{
// ..        
//注册服务单例
services.AddSingleton();
}
公共void配置(IApplicationBuilder应用程序)
{
//创建参与者系统
var system=ActorSystem.Create(“MyActorSystem”);
//将服务单例注入到actor中
directory.MyActorRef
=system.ActorOf(MyActor.Props(app.ApplicationServices.GetService()),“MyActor”);
}

public void配置服务(IServiceCollection服务)
{
// ..        
//注册服务单例
services.AddSingleton();
}
//在Configure中注入它
公共void配置(IApplicationBuilder应用程序、IMyService myService)
{
//创建参与者系统
var system=ActorSystem.Create(“MyActorSystem”);
//将服务单例注入到actor中
directory.MyActorRef
=system.ActorOf(MyActor.Props(myService),“MyActor”);
}
这将在
Configure
中初始化并解析您的服务

关于单例、作用域和参与者的评论 请记住,您无法从
app.ApplicationServices
或服务提供商解析作用域服务,它将引发异常。当您想要使用默认注册为作用域服务的DbContext时,这可能会成为一个问题

您也可以将其注册为覆盖到
AddDbContext
的作用域,但要注意“内存泄漏”,随着跟踪对象数量的增加,内存消耗也会增加(大量跟踪实体(>=10k)将显著减少与跟踪器相关的操作)

记住DbContext,还要记住EF和EF Core不是线程安全的,线程不能访问它们(或者运行多个异步操作,即启动5个查询,不等待,然后使用
wait Task.WaitAll(…)

虽然保证一个参与者在同一时间只能由一个线程访问,但如果您对其进行范围限定,则服务不会被访问


这项工作的好坏取决于Akka.NET使用的任务调度器实现(同样,不熟悉它的内部结构,即Orleans在存储提供程序后面抽象持久性)。

您没有在任何lamda中运行
system.ActorOf(MyActor.Props(provider.GetService(),“MyActor”)
。这直接在函数内部运行,并在web应用程序启动之前创建。感谢您的详细回答。我完全误判了
BuildServiceProvider()
。我将坚持你的第一个建议,直到我做得很好为止。我还补充了一些你可能感兴趣的评论,因为在ASP.NET内核中,单例可能会变得棘手