From a8aa856cba7b5974995d60935c936b7d010a1c8b Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Tue, 26 Mar 2019 17:30:29 -0400 Subject: [PATCH] Make a usable version of getEventByCorrelationId This adds @baronfel's Newtonsoft.Json.FSharp.Idiomatic to provide custom JsonConverter that are more friendly for F# serializing. Now that the json that gets serialized turns out to be sane, we can use raw sql to query the EventRead table easily. Although this doesn't use Martens Linq generation, it's the quickest option to getting a production ready version --- paket.dependencies | 1 + paket.lock | 4 +- .../CosmoStore.Marten.fsproj | 42 ++++++++-------- src/CosmoStore.Marten/EventStore.fs | 50 +++++++++++++++---- src/CosmoStore.Marten/paket.references | 1 + 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/paket.dependencies b/paket.dependencies index 43035b2..24fbd65 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -34,6 +34,7 @@ group Marten nuget TaskBuilder.fs nuget FSharp.Core >= 4.3.4 < 4.5.4 nuget Marten.FSharp + github baronfel/Newtonsoft.Json.FSharp.Idiomatic src/Newtonsoft.Json.FSharp.Idiomatic/Newtonsoft.Json.FSharp.Idiomatic.fs group InMemory source https://api.nuget.org/v3/index.json diff --git a/paket.lock b/paket.lock index 291c103..081d968 100644 --- a/paket.lock +++ b/paket.lock @@ -3475,7 +3475,9 @@ NUGET FSharp.Core (>= 4.1.17) - restriction: || (&& (>= net45) (< net46) (< netstandard1.6)) (&& (< net45) (>= netstandard1.6)) (&& (>= net46) (< netstandard1.6)) (>= net47) NETStandard.Library (>= 1.6.1) - restriction: && (< net45) (>= netstandard1.6) System.ValueTuple (>= 4.4) - restriction: || (&& (>= net45) (< net46) (< netstandard1.6)) (&& (< net45) (>= netstandard1.6)) (&& (>= net46) (< netstandard1.6)) (>= net47) - +GITHUB + remote: baronfel/Newtonsoft.Json.FSharp.Idiomatic + src/Newtonsoft.Json.FSharp.Idiomatic/Newtonsoft.Json.FSharp.Idiomatic.fs (abe8185653e06893623dd3a39b353afc226fa703) GROUP TableStorage STORAGE: NONE NUGET diff --git a/src/CosmoStore.Marten/CosmoStore.Marten.fsproj b/src/CosmoStore.Marten/CosmoStore.Marten.fsproj index 519ef53..12df524 100644 --- a/src/CosmoStore.Marten/CosmoStore.Marten.fsproj +++ b/src/CosmoStore.Marten/CosmoStore.Marten.fsproj @@ -1,23 +1,23 @@  - - - netstandard2.0 - Roman Provazník;Kunjan Dalal - - - - - - - - - - - - - - - - - + + netstandard2.0 + Roman Provazník;Kunjan Dalal + + + + True + paket-files/Newtonsoft.Json.FSharp.Idiomatic.fs + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/CosmoStore.Marten/EventStore.fs b/src/CosmoStore.Marten/EventStore.fs index 4f730de..9700e97 100644 --- a/src/CosmoStore.Marten/EventStore.fs +++ b/src/CosmoStore.Marten/EventStore.fs @@ -1,5 +1,7 @@ namespace CosmoStore.Marten open Npgsql +open Newtonsoft.Json +open Newtonsoft.Json.FSharp.Idiomatic module EventStore = open System @@ -130,16 +132,28 @@ module EventStore = let! events = getEvents store streamId filter return events.Head } + open Microsoft.FSharp.Quotations.Patterns + + let rec private propertyName quotation = + match quotation with + | PropertyGet (_,propertyInfo,_) -> propertyInfo.Name + | Lambda (_,expr) -> propertyName expr + | _ -> "" + + // Get a type safe name in case this changes somehow + let private correlationIdPropName = propertyName <@ fun (x : EventRead) -> x.CorrelationId @> - let private getEventsByCorrelationId (store: IDocumentStore) corrId = + let private getEventsByCorrelationIdQuery = sprintf "where data->>'%s' = ?" correlationIdPropName + + let private getEventsByCorrelationId (store: IDocumentStore) (corrId : Guid) = task { use session = store.LightweightSession() + + let! res = + session + |> Session.sqlTask getEventsByCorrelationIdQuery [| box (corrId.ToString()) |] -// let res = session |> Session.query |> Queryable.filter <@ fun x -> Option.toNullable(x.CorrelationId) = Nullable(corrId) @> |> Seq.toList -//TODO: Option is type is not supported by store. So, it is better to convert option type to Nullable and then put a guard on that. -//TODO: remove this not optimized filter once things converted to nullable. Then above code can be used. Until then don't use in production - let res = session |> Session.query |> Seq.filter (fun x -> x.CorrelationId = Some corrId) |> Seq.toList - return (res) + return res |> Seq.toList } let private getStreams (store: IDocumentStore) streamsRead = task { @@ -154,7 +168,7 @@ module EventStore = |> Queryable.toListTask return (res |> Seq.toList) - } + } let private getStream (store: IDocumentStore) streamId = task { use session = store.LightweightSession() @@ -170,11 +184,27 @@ module EventStore = let userConnStr(conf) = createConnString (conf.Host) (conf.Username) (conf.Password) (conf.Database) + let converters: JsonConverter [] = [| + OptionConverter() + SingleCaseDuConverter() + MultiCaseDuConverter() + |] + + let martenSerializer = + // Need to tell marten to use enums as strings or we have to teach npgsql about our enums when using parameters + let s = Services.JsonNetSerializer(EnumStorage = EnumStorage.AsString) + + for c in converters do + s.Customize(fun s -> s.Converters.Add(c)) + s + let getEventStore (conf: Configuration) = let store = - userConnStr conf - |> string - |> DocumentStore.For + Marten.DocumentStore.For( + fun ds -> + ds.Connection(userConnStr conf |> string) |> ignore + ds.Serializer(martenSerializer) |> ignore + ) let eventAppended = Event() diff --git a/src/CosmoStore.Marten/paket.references b/src/CosmoStore.Marten/paket.references index c2f4715..68485e1 100644 --- a/src/CosmoStore.Marten/paket.references +++ b/src/CosmoStore.Marten/paket.references @@ -2,3 +2,4 @@ group Marten FSharp.Core TaskBuilder.fs Marten.FSharp + File: Newtonsoft.Json.FSharp.Idiomatic.fs \ No newline at end of file