0

我分别使用 Play-Slick 2.5.x 和 3.1.x 版本。我使用 Slick 的代码生成器并从现有数据库中生成 Slick 模型。实际上,我很害羞地承认我是 DB 设计驱动而不是类设计驱动的。

这是初始设置:

  • 下生成的 Slick 模型generated.Tables._
  • 通用 Slick dao 实现
  • 建立在 Generic Slick dao 之上的服务层

这些是我暂时称为“可插入服务”的模式背后的力量,因为它允许将服务层功能插入模型:

  • Play 的控制器和视图必须只能看到 Service 层(而不是 Dao 的),例如UserService
  • 生成的模型,例如UserRow,预期遵守业务层接口,例如 Deadbolt-2 的主题,但不直接实现它。为了能够实现它,需要“太多”,例如UserRow模型类型,UserDao以及潜在的一些业务上下文。
  • 一些UserService方法自然适用于模型UserRow实例,例如loggedUser.rolesloggedUser.changePassword

因此我有:

generated.Tables.scala光滑的模型类:

case class UserRow(id: Long, username: String, firstName: String, 
                   lastName : String, ...) extends EntityAutoInc[Long, UserRow]

dao.UserDao.scala特定于 User 模型的 Dao 扩展和自定义:

@Singleton
class UserDao @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
    extends GenericDaoAutoIncImpl[User, UserRow, Long] (dbConfigProvider, User) {
  //------------------------------------------------------------------------
  def roles(user: UserRow) : Future[Seq[Role]] = {
    val action = (for {
      role <- SecurityRole
      userRole <- UserSecurityRole if role.id === userRole.securityRoleId
      user <- User if userRole.userId === user.id
    } yield role).result

    db.run(action)
  }
}

services.UserService.scala为 Play 应用程序的其余部分提供所有用户操作的服务:

@Singleton
class UserService @Inject()(auth : PlayAuthenticate, userDao: UserDao) {
  // implicitly executes a DBIO and waits indefinitely for 
  // the Future to complete
  import utils.DbExecutionUtils._
  //------------------------------------------------------------------------
  // Deadbolt-2 Subject implementation expects a List[Role] type 
  def roles(user: UserRow) : List[Role] = {
    val roles = userDao.roles(user)
    roles.toList
  }
}

services.PluggableUserService.scala最后是动态地将服务实现附加到模型类型的实际“可插入”模式:

trait PluggableUserService extends be.objectify.deadbolt.scala.models.Subject {
  override def roles: List[Role]
}

object PluggableUserService {
  implicit class toPluggable(user: UserRow)(implicit userService: UserService) 
    extends PluggableUserService {
    //------------------------------------------------------------------------
    override def roles: List[Role] = {
      userService.roles(user)
    }
}

最后可以在控制器中做:

@Singleton
class Application @Inject() (implicit
                             val messagesApi: MessagesApi,
                             session: Session,
                             deadbolt: DeadboltActions,
                             userService: UserService) extends Controller with I18nSupport {
  import services.PluggableUserService._                           

  def index = deadbolt.WithAuthRequest()() { implicit request =>
    Future {
      val user: UserRow = userService.findUserInSession(session)
      // auto-magically plugs the service to the model
      val roles = user.roles 
      // ...
      Ok(views.html.index)
    }
  }                                

是否有任何 Scala 方式可以帮助不必在 Pluggable Service 对象中编写样板代码?Pluggable Service 名称有意义吗?

4

1 回答 1

1

一种常见的变体可能是您的控制器的父特征,它具有以下方面的内容:

def MyAction[A](bodyParser: BodyParser[A] = parse.anyContent)
               (block: (UserWithRoles) => (AuthenticatedRequest[A]) => Future[Result]): Action[A] = {
    deadbolt.WithAuthRequest()(bodyParser) { request =>
    val user: UserRow = userService.findUserInSession(session)
    // this may be as you had it originally
    // but I don't see a reason not to 
    // simply pull it explicitly from db or 
    // to have it in the session together with roles in the first place (as below UserWithRoles class)
    val roles = user.roles 

    block(UserWithRoles(user, roles))(request)
}  

这里房间里的大象是你获得userService实例的方式。好吧,您需要在控制器构造函数中明确要求它(与使用 的方式相同DeadboltActions)。或者,您可以将DeadboltActions,UserService和其他内容捆绑到一个类中(例如ControllerContext?)并将这个单个实例作为一个构造函数参数注入(但这可能是另一个讨论......)。

之后,您的控制器代码将如下所示:

def index = MyAction() { implicit user => implicit request =>
    Future {
      // ...
      Ok(views.html.index)
    }
  } 

两者都是隐式的userrequest这有助于传递到应用程序的内部(通常是这种情况 - 你带来user对象来执行一些业务逻辑)。

它并没有摆脱你PluggableUserService本身(逻辑仍然存在),但它可以帮助你更容易地在你的控制器中的任何地方重用相同的逻辑(根据我的经验,你需要UserRoles任何真实的应用)。

编辑:我觉得我不太明白你的问题。您想避免样板文件,PluggableUserService或者您想避免PluggableUserService在每个控制器中随处使用这种转换(恕我直言,第二个选项是要避免的)?

于 2016-12-16T23:36:05.553 回答