我正在开发一个使用文档数据库的原型(prototype)(目前是 MongoDB,可能会更改),并且发现 .NET 驱动程序有点麻烦,所以我想我应该使用存储库模式抽象数据访问。这应该可以轻松地将我现在使用的任何驱动程序(NoRM、mongodb-csharp、simple-mongob)替换为你的 killer 级 f# mongodb 驱动程序,当它准备好时,它不会太糟糕。 p>
我的问题是关于“添加”操作的。这将对数据库产生一些副作用,因此对 All 的后续调用将会有所不同。我应该关心吗?传统上在 C# 中我不会,但我觉得在 F# 中我应该这样做。
这是通用存储库接口(interface):
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
这是 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
在整个应用程序中,我将使用 IRepository,从而可以轻松更改驱动程序和潜在的数据库。
调用 All 很好,但是使用 Add 我希望返回一个新的存储库实例,而不是返回单元。像这样的东西:
// 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>
问题是,如果我调用 Get,然后调用 Add,原始存储库仍然返回所有文档。示例:
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()
理想情况下,我希望 a1 和 a2 的长度不同,但它们是相同的,因为它们都访问数据库。该应用程序可以运行,用户可以提出他们的问题,但程序员想知道为什么它返回一个新的 IRepository。
那么我应该在类型设计中尝试处理 Add 对数据库的副作用吗?其他人会如何解决这个问题,您是否使用存储库或类似的接口(interface)类或有更好的功能方法?
最佳答案
看起来您正在将不变性应用于影响外部世界状态的函数。无论 F# 实现如何,您认为它在 MongoDB 级别如何工作?如何防止 repo1
看到 repo2
所做的任何更改?如果其他进程影响数据库,会发生什么情况 - 在这种情况下,repo1
和 repo2
都会发生变化吗?
换句话说,想象一个像这样工作的 System.Console
实现。如果 Console.Out.WriteLine
始终返回一个新的不可变对象(immutable对象),那么它将如何与对 Console.In.ReadLine
的调用进行交互?
编辑 tl;dr: 不要这样做。有时副作用是好的。
关于.net - F# 中的存储库模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4305868/