【问题标题】:Dealing with database access in transformer stacks处理变压器堆栈中的数据库访问
【发布时间】:2016-08-28 16:38:21
【问题描述】:

这个问题是关于groundhogpersistent,因为我相信两者都有同样的问题。

假设我有一个变压器Tr m a,它提供了一些功能f :: Int -> Tr m ()。此功能需要数据库访问。我可以在这里使用几个选项,但没有一个是令人满意的。

我可以在Tr 内部的某处放置一个DbPersist 变压器。实际上,我需要把它放在顶部,因为标准转换器没有PersistBackend 实例,而且我仍然需要为我的Tr newtype 编写一个实例。这已经很糟糕了,因为课程远非最小。我还可以解除我所做的每一个数据库操作。

另一个选项是将f 的签名更改为PersistBackend m => Int -> Tr m ()。这将再次需要我的Tr newtype 上的PersistBackend 实例,或者提升。

现在真正的问题来了。如何在已经具有PersistBackend 约束的上下文中运行Tr?无法与Tr 分享。

我可以选择第一个选项并使用一些新的连接池在Tr 内运行实际的DbPersist 转换器(据我所知,没有办法从PersistBackend 上下文中获取池我' m 已经在),或者我可以执行第二个选项并将运行函数设置为runTr :: PersistBackend m => Tr m a -> m a。第二个选项实际上完全没问题,但这里的问题是 DbPersist,最终必须在堆栈中的某个位置,现在位于 Tr 转换器下,并且没有标准的 PersistBackend 实例由Tr 组成的变压器。

这里的正确方法是什么?目前似乎最好的选择是在堆栈中的某个地方使用一个单独的ReaderT,它可以根据请求为我提供连接池,然后在我想访问数据库的任何地方使用该池进行runDbConn。看到DbPersist 基本上已经只是一个ReaderT 我不明白必须这样做的意义。

【问题讨论】:

    标签: database haskell monad-transformers haskell-persistent


    【解决方案1】:

    土拨鼠

    我建议使用他们的master 分支中的latest groundhog。尽管我将要描述的更改似乎已在 2015 年 9 月实施,但还没有发布到 Hackage。但作者似乎已经解决了这个问题。

    提示,PersistBackend 现在是一个更易于实现的类,与以前的数十种方法长的庞然大物相比已大大减少:

    class (Monad m, Applicative m, Functor m, MonadIO m, ConnectionManager (Conn m), PersistBackendConn (Conn m)) => PersistBackend m where
      type Conn m
      getConnection :: m (Conn m)
    
    instance (Monad m, Applicative m, Functor m, MonadIO m, PersistBackendConn conn) => PersistBackend (ReaderT conn m) where
      type Conn (ReaderT conn m) = conn
      getConnection = ask
    

    他们为ReaderT conn m 编写了一个实例(DbPersist 已被弃用并别名为ReaderT conn),如果您选择将ReaderT 放入其中,您可以轻松地为Tr (ReaderT conn) 编写一个实例而不是外面。它不是一个 mtl monad 转换器,因为您必须实例化 Tr m 而不是 Tr,但是这个和他们使用的相关数据类型技巧应该允许您使用自定义 monad 堆栈而不会大惊小怪。

    您选择的任何一个选项都可能需要一些提升。在我个人看来,我会将ReaderT conn 放在堆栈的最外面。这样,mtl 助手仍然可以通过你的大部分堆栈,你可以粘上一个额外的电梯把它带回家。而且,如果您坚持使用 Hackage 上的版本,这似乎是唯一合理的选择,因为否则您将拥有(旧的)单体 PersistBackend 类。

    持久

    Persistent 更简单一点:只要 monad 转换器堆栈包含 ReaderT SqlBackend 并以 IO 终止,您就可以取消对 runSqlPool :: MonadBaseControlIO m => ReaderT SqlBackend m a -> Pool SqlBackend -> m a 的调用。所有的持久化操作都被定义为返回ReaderT backend m a 类型的东西,所以这种设计是可行的。

    【讨论】:

    • 哇,太棒了。为了解决这个问题,我编写了自己的类,几乎完全是土拨鼠中的PersistBackend。你只是省去了我回答我自己问题的麻烦。接受!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-09
    • 2014-07-26
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    • 2016-01-13
    相关资源
    最近更新 更多