快捷搜索:  汽车  科技

七爪网源码交易平台:七爪源码F

七爪网源码交易平台:七爪源码F.AddSingleton<IRavenClientFactory RavenClientFactory>(fun sp -> let configurationSection = config.GetSection("Raven") let url = configurationSection.GetValue("Url") let dbName = configurationSection.GetValue("Database") new RavenClientFactory([ url ] dbName) )从 RavenDB 读取module RavenDb open Raven.Client.Documents open Raven.Client.Json.Serialization.Newt

几年前,我使用了 RavenDB,这是一次很好的体验 - 从那以后它才突飞猛进。 我曾经使用过很多 MongoDB,我厌倦了“SQL 体验”,需要更好的面向 OOP 的东西。 但现在我是 FP 和 DDD,SQL 不会削减它,Mongo 一直对我不利。

七爪网源码交易平台:七爪源码F(1)

RavenDB 入门

首先,我们需要一个本地环境

docker run --rm -d -p 9010:8080 -p 38888:38888 --name ravendb -e RAVEN_Setup_Mode=None -e RAVEN_License_Eula_Accepted=true -e RAVEN_Security_UnsecuredAccessAllowed=PrivateNetwork ravendb/ravendb

那里有一些环境变量,它们是必需的。 RAVEN_Setup_Mode=None 这会在设置过程中选择安全性,本质上是要求 RavenDB 在没有设置过程的情况下启动 - 删除,并且每次创建容器时都必须通过设置向导,这对于自动化来说很痛苦。

RAVEN_License_Eula_Accepted=true 再次,另一个设置过程。 RAVEN_Security_UnsecuredAccessAllowed=PrivateNetwork 这是因为 TLS 和证书身份验证默认开启,这使得服务器无需身份验证即可访问。

连接到 RavenDB

有一个 C# 示例,我想出的是:

module RavenDb open Raven.Client.Documents open Raven.Client.Json.Serialization.NewtonsoftJson type IRavenClientFactory = abstract member Create : unit -> IDocumentStore type RavenClientFactory (urls databaseName) = let lazyClient = Lazy<IDocumentStore>(fun () -> let s = new DocumentStore( Urls = (urls |> List.toArray) Database = databaseName ) s.Conventions.Serialization <- (NewtonsoftJsonSerializationConventions( CustomizeJsonSerializer = (fun serializer -> ()) CustomizeJsonDeserializer = (fun serializer -> ()) )) s.Initialize() ) interface IRavenClientFactory with member this.Create() = lazyClient.Value interface IDisposable with member this.Dispose() = lazyClient.Value.Dispose()

我使用类和接口,因为 DI 仍然很简单,而且我在 F# 中使用 - 无需搞乱 monad 并陷入多个 monad 地狱。

.AddSingleton<IRavenClientFactory RavenClientFactory>(fun sp -> let configurationSection = config.GetSection("Raven") let url = configurationSection.GetValue("Url") let dbName = configurationSection.GetValue("Database") new RavenClientFactory([ url ] dbName) )

从 RavenDB 读取

我只为阅读和写作创建了一个简单的包装器。 无非就是处理稍后出现的额外正交问题。

module RavenDbRepository open Raven.Client.Documents open Raven.Client.Documents.BulkInsert open Raven.Client.Documents.Linq type IRavenRepository<'a> = abstract member Load : id: string -> ct: CancellationToken -> Task<Result<'a ErrorResponse<string>>> type RavenRepository<'a>(factory: IRavenClientFactory) = interface IRavenRepository<'a> with member this.Load id ct = let client = factory.Create() use session = client.openAsyncSession() let! data = session.LoadAsync<'a>(id = id token = ct) return match data :> obj with | null -> DatabaseErrorResponse (EntryNotFound id) |> Error | _ -> Ok data

不用担心 ErrorResponse<'t> ,它只是一个有区别的联合,允许我将异常和其他事件转化为有用的信息,以便在 API 边界上理解。这样如果是暂时性错误,我可以返回 500 并要求客户端重试或其他什么……

这没有什么太令人惊奇的,只是非常标准的东西。

有趣的时间

因此,让我们深入探讨一些更有趣的方面。

  1. 我想要自动创建数据库
  2. 与域对象和值对象的无缝集成

自动创建数据库

为什么?在我的本地开发过程中,我在计算机之间切换,有时我使用我的“大型”计算机 - 也就是塔式 PC - 有时我喜欢一边写代码一边坐着看电视 - 就像我现在在写这篇博客时所做的那样。每次,我都希望我的数据库和我的 API/网站的其他资源能够自动生成它们的种子数据等……

事实证明,在 RavenDB 中,创建新数据库有点麻烦。与 SQL 或 Mongo 不同 - 只需触发查询将创建数据库或自动生成集合 - RavenDB 需要您与维护 API 进行交互。在该链接中,您将看到 C# 示例,让我们让它变得时髦。

module RavenDbRepository open System open System.Threading open System.Threading.Tasks open Raven.Client.Documents open Raven.Client.Documents.BulkInsert open Raven.Client.Documents.Linq open Raven.Client.Exceptions open Raven.Client.Json.Serialization.NewtonsoftJson open Raven.Client.ServerWide.Operations open Raven.Client.ServerWide open Raven.Client.Exceptions.Database open Raven.Client.Documents.Operations open FsToolkit.ErrorHandling open Microsoft.AspNetCore.Hosting open Microsoft.Extensions.Hosting type IRavenClientFactory = abstract member Create : unit -> IDocumentStore abstract member InitialiseDatabase : env: IWebHostEnvironment -> Task type RavenClientFactory (urls databaseName) = let lazyClient = Lazy<IDocumentStore>(fun () -> let s = new DocumentStore( Urls = (urls |> List.toArray) Database = databaseName ) s.Conventions.Serialization <- (NewtonsoftJsonSerializationConventions( CustomizeJsonSerializer = (fun serializer -> ()) CustomizeJsonDeserializer = (fun serializer -> ()) )) s.Initialize() ) let checkForDatabase () = task { let store = lazyClient.Value try let! _ = store.Maintenance.ForDatabase(databaseName).SendAsync(GetStatisticsOperation()) return Ok () with | :? DatabaseDoesNotExistException -> return Error () } let tryCreateDatabase () = Retries.createPolicy<RavenException> 20 |> Retries.executeCustom CancellationToken.None (fun _ -> task { try let store = lazyClient.Value do! store.Maintenance.Server.SendAsync(CreateDatabaseOperation(DatabaseRecord(databaseName))) :> Task with | :? ConcurrencyException -> do! Task.CompletedTask }) let createIfNotExists result : Task = task { match result with | Ok () -> return! Task.CompletedTask | _ -> do! tryCreateDatabase () } interface IRavenClientFactory with member this.Create() = lazyClient.Value member this.InitialiseDatabase env: Task = task { if env.IsDevelopment() then let! r = checkForDatabase () do! createIfNotExists r } interface IDisposable with member this.Dispose() = lazyClient.Value.Dispose()

在这里,你会看到 Retries,它是我写的 Polly 的一个小包装。

这从返回 Result<unit unit> 的 checkForDatabase 开始,本质上如果它返回 Error () 那么我们需要创建一个数据库。

然后我们 createIfNotExists -> tryCreateDatabase 。 tryCreateDatabase 触发 CreateDatabaseOperation 。 如果这引发了 ConcurrencyException 异常,那么我们可以忽略,因为它已经在其他地方创建。 如果异常是 RavenException,那么很可能是因为服务器还没有准备好,只需要重试直到成功。 它在 20 次重试中从未失败,所以这似乎是合理的。

env 是一个 IWebHostEnvironment ,这样我就可以检查我们是否处于开发模式。 像这样完成 ravenClientFactory.InitialiseDatabase env 。

所以,就是这样。

关注七爪网,获取更多APP/小程序/网站源码资源!

猜您喜欢: