在ASP.NET Core中将服务单例注入到actor(Akka.NET)中

时间:2018-04-19 11:14:21

标签: asp.net-core asp.net-core-2.0 akka.net

我正在尝试使用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的实例与注入应用程序其余部分的实例不同 - 即它不是单例。

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

1 个答案:

答案 0 :(得分:2)

这是因为您在ConfigureServices

中创建了一个单独的IoC容器
// 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抽象存储提供商后面的持久性)。