0

我正在尝试将 Persistent 与 Servant 一起使用,因此我无法将 URL 段自动解析为 Persistent 键。相反,我已将路由设置为需要Int64,并且我想使用它来检索记录以执行主键查找。

我发现的所有内容都 指向用于toSqlKey将整数转换为键,因此我尝试编写一个非常简单的函数来为我做到这一点:

runDB :: (MonadBaseControl IO m, MonadIO m) => (SqlPersistT (NoLoggingT (ResourceT m))) a -> m a
runDB actions = do
  filename <- liftIO $ getEnv "SQLITE_FILENAME"
  runSqlite (pack filename) actions

getRecordByKey :: Int64 -> IO (Maybe (Entity Record))
getRecordByKey recordId = runDB $ get (toSqlKey recordId)

不幸的是,这不起作用。我收到以下类型错误:

Couldn't match expected type ‘PersistEntityBackend
                                (Entity Record)’
            with actual type ‘SqlBackend’
In the second argument of ‘($)’, namely ‘get (toSqlKey recordId)’
In the expression: runDB $ get (toSqlKey recordId)
In an equation for ‘getRecordByKey’:
    getRecordByKey recordId = runDB $ get (toSqlKey recordId)

有点理解这个错误——我查找了getand的类型toSqlKey,它们包括相关的约束:

get :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val) => Key val -> ReaderT backend m (Maybe val)
toSqlKey :: ToBackendKey SqlBackend record => Int64 -> Key record

如果我理解正确,backend并且PersistEntityBackend val需要是相同的类型,但toSqlKey强制执行SqlBackend约束,那么类型不匹配。我的直觉告诉我PersistentEntityBackend (Entity Record) 应该是 SqlBackend,但显然我错了。不过,我不知道为什么或如何。

无论如何,我不知道我在该分析中是对还是错,但无论哪种方式,我都不确定如何解决这个问题或这样做的正确方法是什么。给定一个整数,我如何/应该从我的数据库中获取记录?

4

2 回答 2

2

这对我有用(可能取决于你的包的版本......遗憾的是):

{-# LANGUAGE FlexibleContexts #-}
module Stackoverlflow where

import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Logger(NoLoggingT)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Trans.Resource (ResourceT)
import Data.Int (Int64)
import Data.Text (pack)
import Database.Persist.Class (ToBackendKey, get)
import Database.Persist.Sql (SqlBackend, SqlPersistT, toSqlKey)
import Database.Persist.Sqlite(runSqlite)
import Database.Persist.Types (Entity)
import System.Environment (getEnv)

runDB :: (MonadBaseControl IO m, MonadIO m) =>
        (SqlPersistT (NoLoggingT (ResourceT m))) a -> m a
runDB actions = do
  filename <- liftIO $ getEnv "SQLITE_FILENAME"
  runSqlite (pack filename) actions

getRecordByKey :: (MonadIO m, ToBackendKey SqlBackend val, MonadBaseControl IO m) =>
                 Int64 -> m (Maybe val)
getRecordByKey recordId = runDB $ get (toSqlKey recordId)

如您所见,我刚刚添加了很多类型注释(在我删除签名并要求它告诉我之后,GHC 确实这样做了;))

另请注意,我没有您的Record,因此您应该可以轻松摆脱这些... val东西!

于 2015-11-26T08:53:20.483 回答
1

那么直接创建/Key record的实例并直接使用 URL 中的键呢?FromTextToText

{-# LANGUAGE FlexibleContexts               #-}
{-# LANGUAGE UndecidableInstances               #-}
instance ToBackendKey SqlBackend record => FromText (Key record) where
  fromText k = toSqlKey <$> fromText k
instance ToBackendKey SqlBackend record => ToText (Key record) where
  toText = toText . fromSqlKey
于 2015-11-29T13:31:55.927 回答