我有一些这样的小鲷鱼:
data DB b = DB
{_pgsql :: Snaplet Postgresql
,dbCache :: Map Text Text
}
我希望从postgresql数据库中填充dbCache
。似乎在小快照初始化期间执行此操作是很自然的。
initDB :: SnapletInit b (DB b)
initDB = makeSnaplet "db" "cached database" Nothing $ do
pgs <- nestSnaplet "pgsql" pgsql pgsInit
cache <- getSomeDataPlease pgs
return $ DB pgs cache
那么,问题是:如何在 monad 中使用pgs :: Snaplet Postgres
Initializer
从数据库中读取数据?
snaplet-postgresql-simple提供的DB访问函数在作为HasPostgres
类型类实例的任何monad中运行。 通常,这将是您的应用程序Handler
的单子。
您不能在Initializer
中使用Handler
函数。 初始值设定项 monad 的全部意义在于设置运行 Web 服务器和处理程序 monad 所需的初始状态数据类型。 因此,在初始值设定项中运行处理程序确实是不可能的 - 当然,除非您从另一个Web服务器内部运行一个Web服务器...伊克。
所以你有两种可能的选择。 您可以为Initializer
创建一个HasPostgres
实例。 但这没有多大意义,除非您连接到静态服务器。 如果您正在进行调试,这可能是可以接受的。 有时我会为 IO 执行此操作,以使测试我的数据库函数变得微不足道:
instance HasPostgres IO where
getPostgresState = do
pool <- createPool (connect $ ConnectInfo "127.0.0.1" ...) ...
return $ Postgres pool
但一般来说,制作这样的实例用于生产代码是没有意义的。 这意味着,如果你想在Initializer
中访问数据库,你必须直接使用 postgresql-simple 函数,而不是 snaplet-postgresql-simple 提供的包装器。 这就是我导出 pgPool 访问器函数的原因。 它将看起来像这样:
initDB :: SnapletInit b (DB b)
initDB = makeSnaplet "db" "cached database" Nothing $ do
pgs <- nestSnaplet "pgsql" pgsql pgsInit
let pool = pgPool $ extract pgs
results <- liftIO $ withResource pool (conn -> query_ conn myQuery)
你可以在snaplet-postgresql-simple的auth后端中看到一个真实的例子。
更新:
我刚刚上传了一个新版本的snaplet-postgresql-simple到hackage,它为ReaderT提供了一个HasPostgres实例。 这使您可以使用runReaderT更轻松地完成此操作。 文档中有一个小的代码片段。