我正在研究原型使用文档数据库(目前MongoDB,可能会改变),并发现.NET驱动程序有点痛苦,所以我想我会用Repository模式抽象数据访问.这应该可以很容易地将你正在使用的任何驱动程序(NoRM,mongodb-csharp,simple-mongob)替换为你的杀手f#mongodb驱动程序,它在准备就绪时不会吮吸.
我的问题是关于添加操作.这将对数据库产生一些副作用,因此对All的后续调用将会有所不同.我应该关心吗?在C#传统上我不会,但我觉得在F#我应该.
这是通用存储库接口:
type IRepository<'a> =
interface
abstract member All : unit -> seq<'a>
// Add has a side-effect of modifying the database
abstract member Add : 'a -> unit
end
Run Code Online (Sandbox Code Playgroud)
以下是MongoDB实现的外观:
type Repository<'b when 'b : not struct>(server:MongoDB.IMongo,database) =
interface IRepository<'b> with
member x.All() =
// connect and return all
member x.Add(document:'b) =
// add and return unit
Run Code Online (Sandbox Code Playgroud)
在整个应用程序中,我将使用IRepository,从而可以轻松更改驱动程序和可能的数据库.
调用All很好,但是添加我希望的是而不是返回单元,返回一个新的存储库实例.就像是:
// Add has a side-effect of modifying the database
// but who cares as we now return a new repository
abstract member Add : 'a -> IRepository<'a>
Run Code Online (Sandbox Code Playgroud)
问题是如果我调用Get,然后添加,原始存储库仍然会返回所有文档.例:
let repo1 = new Repository<Question>(server,"killerapp") :> IRepository<Question>
let a1 = repo1.All()
let repo2 = repo1.Add(new Question("Repository pattern in F#"))
let a2 = repo2.All()
Run Code Online (Sandbox Code Playgroud)
理想情况下,我希望a1和a2的长度不同,但它们与数据库中的长度相同.应用程序工作,用户可以问他们的问题,但程序员不知道为什么它返回一个新的IRepository.
那么我应该尝试在类型设计中处理Add对数据库的副作用吗?其他人如何解决这个问题,您是使用Repository还是这样的接口类还是有更好的功能方法?
看起来您正在将不变性应用于影响外部世界状态的函数。无论 F# 实现如何,您认为它在 MongoDB 级别如何工作?您将如何防止repo1看到repo2所做的任何更改?如果其他进程影响数据库,会发生什么情况——在这种情况下同时执行repo1并更改?repo2
换句话说,想象一下System.Console这样的实现。如果Console.Out.WriteLine始终返回一个新的不可变对象,它将如何与 的调用交互Console.In.ReadLine?
编辑 tl;dr:不要这样做。有时副作用是好的。
| 归档时间: |
|
| 查看次数: |
1586 次 |
| 最近记录: |