我正在尝试使用cached
函数来阻止不同小部件和处理程序中的多个数据库查询:
newtype CachedBobId key
= CachedBobId { unCachedBobId :: key }
deriving Typeable
getBob' :: Handler BobId
getBob' = do
uncle <- runInputGet $ ireq textField "bobsuncle"
(Entity bob _) <- runDB $ getBy404 $ UniqueBob uncle
return bob
getBob :: Handler BobId
getBob = do
a <- getBob'
let b = return $ CachedBobId a
c <- cached b
return $ unCachedBobId c
在某个小部件中:
renderDerp :: Widget
renderDerp = do
--these are used in the shakespeare files
lolBob <- handlerToWidget $ getBob
nutherBob <- handlerToWidget $ getBob
$(widgetFile "test")
这会编译,但获取ID的查询仍会多次运行。
我做错了什么?或者,有没有更好的方法只能获得bob一次并在每个处理程序和小部件中使用它?
答案 0 :(得分:3)
我对Yesod很新,但我认为你只需要调整getBob
getBob :: Handler BobId
getBob = unCachedBobId <$> cached (CachedBobId <$> getBob')
问题是,您当前的getBob
函数会使用do
启动其a <- getBob'
块。请记住,do
块会对monadic操作进行排序,因此每次调用getBob'
时,您实际上最终都会调用getBob
。具有讽刺意味的是,在您完成此操作之后,您将创建一个处理程序的缓存版本,它返回您从getBob'
获得的内容,但最终只查询缓存版本一次(之后使用{{ 1}}),然后它就会超出范围,垃圾收集器就会得到它。
在我上面提到的解决方案中,您可以在c <- cached b
中包含getBob'
给您的任何内容。然后,您将该处理程序CachedBobId
传递给CachedBobId <$> getBob' :: Handler (CachedBobId BobId)
,它会返回另一个相同类型的处理程序cached
,但会使用缓存。最后,提取缓存处理程序为您提供的任何内容都可以返回cached (CachedBobId <$> getBob')
。