I am running a Scala Play 2.2 application with Slick 1.0.1. I am trying to wrap all of my database calls into a future try, for example:
object DbTeachers extends Table[DbTeacher]("edu_teachers") {
...
def insertTeacher(school: Int, userId: String)
(implicit ec: ExecutionContext, db: Database) =
future { Try { db.withSession => { implicit s: Session =>
(DbTeachers.school ~ DbTeachers.teacher).insert(school, userId)
}}}
}
I find that the pattern future { Try { db.withSession => { ACTUAL_CODE_GOES_HERE }}}
creates clutter and I would like to abstract it out as follows:
sealed class DbAsync[T](block: => T) {
import play.api.libs.concurrent.Execution.Implicits.defaultContext
implicit lazy val db = Database.forDataSource(DB.getDataSource())
def get: Future[Try[T]] = future { Try { db.withSession { implicit s: Session =>
block
}}}
}
object DbAsync {
def apply[T](block: => T): Future[Try[T]] = new DbAsync[T](block).get
}
And then I can write my insertTeacher function as:
def insertTeacher(school: Int, userId: String) = DbAsync {
(DbTeachers.school ~ DbTeachers.teacher).insert(school, userId)
}
However, the scala compiler (2.10.2) complains about this: could not find implicit value for parameter session: scala.slick.session.Session
According to my understanding, the insert()
method does have an implicit session variable in scope within the DbAsync block, and because it is a call-by-name parameter, it shouldn't actually be evaluated until it is called within the DbAsync, at which time there would be an implicit session object in scope.
So, my question is, how do I convince the Scala compiler that there actually is an implicit Session object in scope?
Your suggestion is incorrect. It doesn't matter where call-by-name
parameter will be evaluated. All implicit parameters should be resolved at compile time in the place where they are required.
You could make it work this way:
def dbAsync[T](block: Session => T): Future[Try[T]] = {
import play.api.libs.concurrent.Execution.Implicits.defaultContext
implicit lazy val db = Database.forDataSource(DB.getDataSource())
future { Try { db.withSession { block }}}
}
def insertTeacher(school: Int, userId: String) = dbAsync { implicit s: Session =>
(DbTeachers.school ~ DbTeachers.teacher).insert(school, userId)
}
Note that you don't need class DbAsync
nor object DbAsync
.
Note that you should not use defaultContext
for blocking operations. You could create additional ExecutionContext
with configured thread pool.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With