我有一个使用SqLite和sclick的play-scala应用程序。我的表定义如下:
@Singleton
class DataSets @Inject()(protected val dbConfigProvider: DatabaseConfigProvider, keys: PublicKeys) extends DataSetsComponent
with HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
val DataSets = TableQuery[DataSetsTable]
def all = db.run(DataSets.sortBy { _.id }.result)
...
}
我的控制器可以通过DI访问:
@Singleton
class DataSetsController @Inject() (dataSets: DataSets, env: play.Environment) extends Controller {
...
如何在Actor中获取数据库句柄?
class TrainActor @Inject() (dataSets: DataSets) extends Actor {
...
当然不起作用,因为Guice没有找到DataSets类。
编辑:澄清:我不想在控制器中使用actor进行数据库访问(通过ask),但是在请求之后从控制器启动一些资源密集型计算并在之后将它们存储在db中(异步)
答案 0 :(得分:1)
我现在找到了一种与DI集成的方法,紧跟http://www.w3schools.com/tags/tag_object.asp之后。因为ActorContext
的需要,InjectedActorSupport
只能由Actor
继承。这意味着我必须创建一个演员,除了实例化并启动新的" worker"演员。也许有一种更简单的方法,但这可以正常工作。
TrainActor.scala
:
package actors
import javax.inject.Inject
import akka.actor._
import com.google.inject.assistedinject.Assisted
import models.{DataSet, Model, PublicKey}
import play.api.Logger
import tables.DataSets
import scala.concurrent.ExecutionContext.Implicits.global
object TrainActor {
case object Start
case class LoadData(d: DataSet, k: PublicKey)
trait Factory {
def apply(model: Model): Actor
}
}
class TrainActor @Inject() (val dataSets: DataSets, @Assisted val model: Model) extends Actor {
import TrainActor._
def receive = {
case Start =>
dataSets.findWithKey(model.id.get)
...
TrainActorStarter.scala
:
package actors
import javax.inject.Inject
import akka.actor.{Actor, ActorRef}
import models.Model
import play.api.libs.concurrent.InjectedActorSupport
object TrainActorStarter {
case class StartTraining(model: Model)
}
/**
* https://www.playframework.com/documentation/2.5.x/ScalaAkka#Dependency-injecting-actors
* @param childFactory
*/
class TrainActorStarter @Inject() (childFactory: TrainActor.Factory) extends Actor with InjectedActorSupport {
import TrainActorStarter._
def receive = {
case StartTraining(model: Model) =>
val trainer: ActorRef = injectedChild(childFactory(model), s"train-model-model-${model.id.get}")
trainer ! TrainActor.Start
}
}
ActorModule.scala
:
package actors
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class ActorModule extends AbstractModule with AkkaGuiceSupport {
def configure(): Unit = {
bindActor[TrainActorStarter]("train-actor-starter")
bindActorFactory[TrainActor, TrainActor.Factory]
}
}
最后在控制器中:
package controllers
import javax.inject._
import actors.{TrainActorStarter, TrainCallbackActor}
import akka.actor.{ActorRef, ActorSystem, _}
import akka.stream.Materializer
...
@Singleton
class ModelsController @Inject() (implicit system: ActorSystem, materializer: Materializer, ..., @Named("train-actor-starter") trainActorStarter: ActorRef) extends Controller with InjectedActorSupport {
def startTraining(model: Model): Unit = {
if(model.id.isEmpty) return
trainActorStarter ! TrainActorStarter.StartTraining(model)
}
答案 1 :(得分:0)
您可以将依赖项注入actor:
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class MyModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[TrainActor]("injected-train-actor")
}
}
之后只需将actor注入控制器:
class MyController @Inject()(@Named("injected-train-actor") trainActor: ActorRef) {
def endpointTest = Action.async {
for {
items <- (trainActor ? FetchAll).mapTo[Seq[DataSetsTableRow]]
} yield Ok(Json.toJson(items))
}
}
答案 2 :(得分:0)
而不是
@Singleton
class DataSets
可以将它声明为可以充当DataSetsDAO的简单scala对象
object DataSets
然后在演员只需使用DataSets.dbOperation
时请记住,结果类型将是Future,所以只需在onComplete
上的演员中安排自己的消息以避免任何副作用。