objects |> functions


Tres - Step 4

Dependencies

As with Dos, we need reference to Nancy.Session.RavenDB and MiniGuid, and we also are going to need a package called TaskBuilder.fs. All of these are covered by the packages we already reference in paket.dependencies, so we just need to add these three packages to paket.references, then run paket install.

Boostrapper Updates

We need to add the Certificate and Password properties to our DataConfig record, so that we can use them to configure our RavenDB connection. It becomes:

1: 
2: 
3: 
4: 
5: 
6: 
type DataConfig =
  { Url         : string
    Database    : string
    Certificate : string
    Password    : string
    }

Then, we'll convert our bootstrapper changes to F#. The bootstrapper is at the top of App.fs. We'll need to add some open statements...

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
open Nancy.Session.Persistable
open Nancy.Session.RavenDB
open System.Security.Cryptography.X509Certificates

type TresBootstrapper () =
  inherit DefaultNancyBootstrapper ()

  let _store =
    (lazy
      (let cfg = File.ReadAllText "data-config.json" |> JsonConvert.DeserializeObject<DataConfig>
      (new DocumentStore (
        Urls        = cfg.Urls,
        Database    = cfg.Database,
        Certificate =
          match isNull cfg.Certificate || cfg.Certificate = "" with
          | true -> null
          | false -> new X509Certificate2(cfg.Certificate, cfg.Password))).Initialize ()
    )).Force ()

This is F#'s version of lazy initialization. There are a lot more parentheses here, as we're creating objects and then calling into their properties or methods. That's just a feature of how F# works. In C#, new DocumentStore().Initialize() will work; in F#, though, we have to set off the creation of the document store like (new DocumentStore ()).Initialize (). (The space before the () is optional, and these extra parentheses are required whether it's there or not.)

Now, we can chage ConfigureApplicationContainer and bring across ApplicationStartup.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
  override __.ConfigureApplicationContainer container =
    base.ConfigureApplicationContainer container
    container.Register<IDocumentStore> _store |> ignore
  
  override __.ApplicationStartup (container, pipelines) =
    base.ApplicationStartup(container, pipelines);
    IndexCreation.CreateIndexes (typeof<Categories_ByWebLogIdAndSlug>.Assembly, _store)
    PersistableSessions.Enable (pipelines, RavenDBSessionConfiguration _store)

These are the same chages, just translated to F#.

Testing

As with Dos, we need to bring in the session extension method. Add open Nancy.Session.Persistable to the group of open statements. C# only lets you define extension methods; however, F# allows you to define properties as well, so PersistableSession is implemented as a property on Nancy's Request object. Then, we translate the function:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
    this.Get("/", fun _ ->
        let count =
          (this.Request.PersistableSession.Get<Nullable<int>> "Count"
           |> Option.ofNullable
           |> function Some x -> x | None -> 0) + 1
        this.Request.PersistableSession.["Count"] <- count
        (sprintf "You have visited this page %i times this session" count) :> obj)

Since F# is designed to use Options to represent a value that may or may not be present (rather than null checks), but Nancy's sessions are designed to return null if a value isn't found, this is a bit more work. We have to specify the full type (Nullable<int>), as F# doesn't support the trailing question mark syntax. Then, we use the Option module's ofNullable function to convert a nullable value into an option. Finally, we use an inline function, which behaves the same way as a match statement on the value it receives via the pipe operator.

As with Uno and Dos, if you run the site, you should be able to refresh and see the counter increase.

Data Seeding

The bulk of this process is simply bringing over the HomeModule.Seed method from Dos. However, before we take a look at it, we need to take a quick side trip to discuss a concept we've seen in Cinco but have not explained yet.

Computation Expressions

In F#, a computation expression (CE) is an expression that can have one or more steps that produce a result. For each type of result, there is a different type of CE. For example, there is a seq CE that generates a sequence (F#'s term for IEnumerable<T>). Within a CE, you will find regular F# keywords such as let, do, and match; you will also likely find a return keyword (which we don't normally use, because a function's last value is its implied return value). You will also see keywords followed by an exclamation point - let!, do!, match!, and return!; these keywords will have special meaning, and will directly affect whatever computation is being determined.

The seq CE also has a yield keyword, that acts like yield return does in C#; the yield! keyword yields each of the items of an existing sequence. Below is an example seq CE, which contains the parts of a fake US phone number.

1: 
2: 
3: 
4: 
5: 
    seq {
      yield "123"
      yield "555"
      yield "6789"
      }

We could combine these using let phone = x |> Seq.reduce (+).

The task CE

F# provides an async CE that is used to define a workflow that can execute asynchronous processes together with others to produce a result. However, F#'s Async behaves a bit differently than the .NET Task and Task<T> types. F#'s Async module does have functions like Async.AwaitTask that bring tasks into the F# async world. But, if we were to write an await function, it would look something like:

1: 
  let await task = task |> Async.AwaitTask |> Async.RunSynchronously

This makes something like let x = await (SomethingReturningATask()) or let x = await <| SomethingReturningATask(). However, that code doesn't flow very well; we either have to wrap the function/method calls in parentheses, or use pipeline operators to send it to await. Also, this only works for Task<T>; we have to write a different one to handle the non-generic Task.

Enter the task CE, provided by TaskBuilder.fs. It became popular as part of the Giraffe project, as Giraffe's sitting atop ASP.NET Core required it to deal with tasks. Within a task CE, let! awaits a generic task, do! awaits a non-generic task, match! awaits a generic task, then pattern-matches on the result, and return! awaits a generic task and returns the result. return returns a regular value as the CE's result. The type of the task CE is exactly what the rest of the .NET environment expects.

Take a look at the seed function to see the task CE in action. open FSharp.Control.Tasks.V2.ContextInsensitive brings it into scope, and these tasks will be run with .ConfigureAwait(false); this keeps tasks from deadlocking from waiting on the same execution context.

One final note - the :> obj at the bottom of the seed function makes the return type Task<obj> instead of Task<string>, which is the required signature for NancyModule's Get method.

But Wait, There's More!

Go ahead and run it at this point, go to http://localhost:5000/seed, and compare the RavenDB documents for posts or pages against the ones created for Dos. You'll notice that the content types (our IArticleContent fields) do not have the ContentType and Text properties! For some reason, this is serialized differently if it's part of an F# record type. (This may be an edge-case bug; if I can isolate the behavior, I'll submit an issue.) However, this gives us a chance for an early look at writing our own custom JsonConverters. In step 3 Quatro, we added FSharpLu's compact DU converter to RavenDB's configuration; this time, we'll write our own.

Just below the definition of MarkdownArticleContent, we'll create IArticleContentConverter, which will extend JsonConverter<IArticleContent>. Generic converters need to implement two methods, WriteJson and ReadJson. Writing is the easiest; and, we'll use the ContentType property to write a very succinct JSON object in of the form { "[Html|Markdown]" : [text] }. Here's the declaration, up through WriteJson.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
type IArticleContentConverter () =
  inherit JsonConverter<IArticleContent> ()
  
  override __.WriteJson (w : JsonWriter, v : IArticleContent, _ : JsonSerializer) =
    w.WriteStartObject ()
    w.WritePropertyName v.ContentType
    w.WriteValue v.Text
    w.WriteEndObject ()

Each Write* call in Json.NET writes a token; these tokens describe the shape of the JSON that will eventually be serialized. Since we're intercepting a single property, but writing an object, we write the start object token. Then, we use ContentType for the property name, and Text for the value. We also need to write an end object token.

Reading it back is a bit different:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
  override __.ReadJson (r : JsonReader, _, _, _, _) =
    let typ  = r.ReadAsString () // PropertyName
    let text = r.ReadAsString () // String
    (r.Read >> ignore) () // EndObject
    let content : IArticleContent =
      match typ with
      | ContentType.Html -> upcast HtmlArticleContent ()
      | ContentType.Markdown -> upcast MarkdownArticleContent ()
      | x -> invalidOp (sprintf "Cannot deserialize %s into IArticleContent" x)
    content.Text <- text
    content

When ReadJson is called, the starting object token has already been read. We read the tokens back in the order in which we wrote them, so as we read through the JsonReader, we'll encounter our property name first, then the value token. (We also need one more read to advance the reader past the end object token.) Once we have our property values, the ContentType tells us what type of implementation to construct, and we can use the Text setter on the IArticleContent interface to put the text in the object.

A bit of encouragement may be good here - I learned about what I've explained in this section when I encountered this problem for this project, and what you see above is the second version of it. I had written simple transformation JsonConverters before, and the others we'll write will be like that. To me, this has been an illustration that no language is immune from the occasional odd behavior; but, if we apply what we already know, and do a bit of documentation diving, we'll often find an elegant solution. We might even no longer classify the behavior we encountered as odd!

We do need to revisit our RavenDB connection, though, as we need to actually plug our converter into the document store. Here's the updated definition of _store from App.fs:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
  let _store =
    (lazy
     (let cfg = File.ReadAllText "data-config.json" |> JsonConvert.DeserializeObject<DataConfig>
      let store =
        new DocumentStore (
          Urls        = cfg.Urls,
          Database    = cfg.Database,
          Certificate =
            match isNull cfg.Certificate || cfg.Certificate = "" with
            | true -> null
            | false -> new X509Certificate2(cfg.Certificate, cfg.Password))
      store.Conventions.CustomizeJsonSerializer <-
        fun x -> x.Converters.Add (IArticleContentConverter ())
      store.Initialize ()
    )).Force ()

At this point, use RavenDB studio to delete the existing documents, re-run the application, and re-seed the database. You should now see ContentType and Text fields under the page/post's Text and Revision properties.

Conclusion

We did another C# to F# translation. We also learned about computation expressions in general, the task CE specifically, and how to write a non-trivial JSON converter. Nice job - now, on to Quatro!


Back to step 4

namespace Newtonsoft
namespace Newtonsoft.Json
namespace Raven
namespace Raven.Client
namespace Raven.Client.Documents
namespace Raven.Client.Documents.Indexes
namespace System
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type Categories_ByWebLogIdAndSlug =
  inherit AbstractJavaScriptIndexCreationTask
  new : unit -> Categories_ByWebLogIdAndSlug

--------------------
new : unit -> Categories_ByWebLogIdAndSlug
val this : Categories_ByWebLogIdAndSlug
Multiple items
type AbstractJavaScriptIndexCreationTask =
  inherit AbstractIndexCreationTask
  member CreateIndexDefinition : unit -> IndexDefinition
  member Fields : Dictionary<string, IndexFieldOptions> with get, set
  member IsMapReduce : bool
  member Maps : HashSet<string> with get, set

--------------------
AbstractJavaScriptIndexCreationTask() : AbstractJavaScriptIndexCreationTask
property AbstractJavaScriptIndexCreationTask.Maps: HashSet<string>
Multiple items
type HashSet<'T> =
  new : unit -> HashSet<'T> + 3 overloads
  member Add : item:'T -> bool
  member Clear : unit -> unit
  member Comparer : IEqualityComparer<'T>
  member Contains : item:'T -> bool
  member CopyTo : array:'T[] -> unit + 2 overloads
  member Count : int
  member ExceptWith : other:IEnumerable<'T> -> unit
  member GetEnumerator : unit -> Enumerator<'T>
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  ...
  nested type Enumerator

--------------------
HashSet() : HashSet<'T>
HashSet(comparer: IEqualityComparer<'T>) : HashSet<'T>
HashSet(collection: IEnumerable<'T>) : HashSet<'T>
HashSet(collection: IEnumerable<'T>, comparer: IEqualityComparer<'T>) : HashSet<'T>
Multiple items
val string : value:'T -> string

--------------------
type string = System.String
Multiple items
type AutoOpenAttribute =
  inherit Attribute
  new : unit -> AutoOpenAttribute
  new : path:string -> AutoOpenAttribute
  member Path : string

--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
Multiple items
type RequireQualifiedAccessAttribute =
  inherit Attribute
  new : unit -> RequireQualifiedAccessAttribute

--------------------
new : unit -> RequireQualifiedAccessAttribute
Multiple items
type LiteralAttribute =
  inherit Attribute
  new : unit -> LiteralAttribute

--------------------
new : unit -> LiteralAttribute
val Html : string
val Markdown : string
type IArticleContent =
  interface
    abstract member Generate : unit -> string
    abstract member ContentType : string
    abstract member Text : string
    abstract member Text : string with set
  end
module ContentType

from Tres.Domain
val set : elements:seq<'T> -> Set<'T> (requires comparison)
type unit = Unit
Multiple items
type HtmlArticleContent =
  interface IArticleContent
  new : unit -> HtmlArticleContent
  override ToString : unit -> string

--------------------
new : unit -> HtmlArticleContent
val mutable text : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
val __ : HtmlArticleContent
val v : string
Multiple items
type MarkdownArticleContent =
  interface IArticleContent
  new : unit -> MarkdownArticleContent
  override ToString : unit -> string

--------------------
new : unit -> MarkdownArticleContent
val __ : MarkdownArticleContent
DataConfig.Url: string
DataConfig.Database: string
DataConfig.Certificate: string
DataConfig.Password: string
Multiple items
type JsonIgnoreAttribute =
  inherit Attribute
  new : unit -> JsonIgnoreAttribute

--------------------
JsonIgnoreAttribute() : JsonIgnoreAttribute
val this : DataConfig
module Indexes

from Tres
namespace Nancy
namespace System.IO
namespace Nancy.Session
namespace Nancy.Session.Persistable
namespace Nancy.Session.RavenDB
namespace System.Security
namespace System.Security.Cryptography
namespace System.Security.Cryptography.X509Certificates
Multiple items
type TresBootstrapper =
  inherit DefaultNancyBootstrapper
  new : unit -> TresBootstrapper
  override ApplicationStartup : container:TinyIoCContainer * pipelines:IPipelines -> unit
  override ConfigureApplicationContainer : container:TinyIoCContainer -> unit

--------------------
new : unit -> TresBootstrapper
Multiple items
type DefaultNancyBootstrapper =
  inherit NancyBootstrapperWithRequestContainerBase<TinyIoCContainer>
  new : unit -> DefaultNancyBootstrapper
  member GetEnvironment : unit -> INancyEnvironment
  static val DefaultAutoRegisterIgnoredAssemblies : IEnumerable<Func<Assembly, bool>>

--------------------
DefaultNancyBootstrapper() : DefaultNancyBootstrapper
val _store : IDocumentStore
val cfg : DataConfig
type File =
  static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
  static member AppendAllText : path:string * contents:string -> unit + 1 overload
  static member AppendText : path:string -> StreamWriter
  static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
  static member Create : path:string -> FileStream + 3 overloads
  static member CreateText : path:string -> StreamWriter
  static member Decrypt : path:string -> unit
  static member Delete : path:string -> unit
  static member Encrypt : path:string -> unit
  static member Exists : path:string -> bool
  ...
File.ReadAllText(path: string) : string
File.ReadAllText(path: string, encoding: System.Text.Encoding) : string
type JsonConvert =
  static val True : string
  static val False : string
  static val Null : string
  static val Undefined : string
  static val PositiveInfinity : string
  static val NegativeInfinity : string
  static val NaN : string
  static member DefaultSettings : Func<JsonSerializerSettings> with get, set
  static member DeserializeAnonymousType<'T> : value:string * anonymousTypeObject:'T -> 'T + 1 overload
  static member DeserializeObject : value:string -> obj + 7 overloads
  ...
JsonConvert.DeserializeObject<'T>(value: string) : 'T
JsonConvert.DeserializeObject(value: string) : obj
JsonConvert.DeserializeObject<'T>(value: string, settings: JsonSerializerSettings) : 'T
JsonConvert.DeserializeObject<'T>(value: string, [<System.ParamArray>] converters: JsonConverter []) : 'T
JsonConvert.DeserializeObject(value: string, type: System.Type) : obj
JsonConvert.DeserializeObject(value: string, settings: JsonSerializerSettings) : obj
JsonConvert.DeserializeObject(value: string, type: System.Type, settings: JsonSerializerSettings) : obj
JsonConvert.DeserializeObject(value: string, type: System.Type, [<System.ParamArray>] converters: JsonConverter []) : obj
type DataConfig =
  {Url: string;
   Database: string;
   Certificate: string;
   Password: string;}
    member Urls : string []
Multiple items
type DocumentStore =
  inherit DocumentStoreBase
  new : unit -> DocumentStore
  member AggressivelyCacheFor : cacheDuration:TimeSpan * ?database:string -> IDisposable + 1 overload
  member BulkInsert : ?database:string * ?token:CancellationToken -> BulkInsertOperation
  member Changes : ?database:string -> IDatabaseChanges + 1 overload
  member DisableAggressiveCaching : ?database:string -> IDisposable
  member Dispose : unit -> unit
  member GetLastDatabaseChangesStateException : ?database:string * ?nodeTag:string -> Exception
  member GetRequestExecutor : ?database:string -> RequestExecutor
  member Identifier : string with get, set
  member Initialize : unit -> IDocumentStore
  ...

--------------------
DocumentStore() : DocumentStore
property DataConfig.Urls: string []
val isNull : value:'T -> bool (requires 'T : null)
Multiple items
type X509Certificate2 =
  inherit X509Certificate
  new : unit -> X509Certificate2 + 12 overloads
  member Archived : bool with get, set
  member Extensions : X509ExtensionCollection
  member FriendlyName : string with get, set
  member GetNameInfo : nameType:X509NameType * forIssuer:bool -> string
  member HasPrivateKey : bool
  member Import : rawData:byte[] -> unit + 5 overloads
  member IssuerName : X500DistinguishedName
  member NotAfter : DateTime
  member NotBefore : DateTime
  ...

--------------------
X509Certificate2() : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte []) : X509Certificate2
   (+0 other overloads)
X509Certificate2(fileName: string) : X509Certificate2
   (+0 other overloads)
X509Certificate2(handle: nativeint) : X509Certificate2
   (+0 other overloads)
X509Certificate2(certificate: X509Certificate) : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte [], password: string) : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte [], password: System.Security.SecureString) : X509Certificate2
   (+0 other overloads)
X509Certificate2(fileName: string, password: string) : X509Certificate2
   (+0 other overloads)
X509Certificate2(fileName: string, password: System.Security.SecureString) : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte [], password: string, keyStorageFlags: X509KeyStorageFlags) : X509Certificate2
   (+0 other overloads)
val container : TinyIoc.TinyIoCContainer
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>() : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>() : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(factory: System.Func<TinyIoc.TinyIoCContainer,TinyIoc.NamedParameterOverloads,'RegisterType>) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>(instance: 'RegisterImplementation) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(instance: 'RegisterType) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>(name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register(registerType: System.Type) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(factory: System.Func<TinyIoc.TinyIoCContainer,TinyIoc.NamedParameterOverloads,'RegisterType>, name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>(instance: 'RegisterImplementation, name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
type IDocumentStore =
  inherit IDisposalNotification
  inherit IDisposable
  member AggressivelyCache : ?database:string -> IDisposable
  member AggressivelyCacheFor : cacheDuration:TimeSpan * ?database:string -> IDisposable + 1 overload
  member BulkInsert : ?database:string * ?token:CancellationToken -> BulkInsertOperation
  member Certificate : X509Certificate2
  member Changes : ?database:string -> IDatabaseChanges + 1 overload
  member Conventions : DocumentConventions
  member Database : string with get, set
  member DisableAggressiveCaching : ?database:string -> IDisposable
  member ExecuteIndex : task:AbstractIndexCreationTask * ?database:string -> unit
  member ExecuteIndexAsync : task:AbstractIndexCreationTask * ?database:string * ?token:CancellationToken -> Task
  ...
val ignore : value:'T -> unit
val __ : TresBootstrapper
val pipelines : Bootstrapper.IPipelines
type IndexCreation =
  static member CreateIndexes : assemblyToScan:Assembly * store:IDocumentStore * ?conventions:DocumentConventions * ?database:string -> unit + 1 overload
  static member CreateIndexesAsync : assemblyToScan:Assembly * store:IDocumentStore * ?conventions:DocumentConventions * ?database:string * ?token:CancellationToken -> Task + 1 overload
IndexCreation.CreateIndexes(indexes: System.Collections.Generic.IEnumerable<AbstractIndexCreationTask>, store: IDocumentStore,?conventions: Conventions.DocumentConventions,?database: string) : unit
IndexCreation.CreateIndexes(assemblyToScan: System.Reflection.Assembly, store: IDocumentStore,?conventions: Conventions.DocumentConventions,?database: string) : unit
val typeof<'T> : System.Type
Multiple items
type PersistableSessions =
  new : cfg:IPersistableSessionConfiguration -> PersistableSessions
  member Load : request:Request -> IPersistableSession
  member Save : session:IPersistableSession -> response:Response -> unit
  static member Enable : pipelines:IPipelines * cfg:IPersistableSessionConfiguration -> unit
  static member private LoadSession : ctx:NancyContext -> provider:PersistableSessions -> Response
  static member private SaveSession : ctx:NancyContext -> provider:PersistableSessions -> unit

--------------------
new : cfg:IPersistableSessionConfiguration -> PersistableSessions
static member PersistableSessions.Enable : pipelines:Bootstrapper.IPipelines * cfg:IPersistableSessionConfiguration -> unit
Multiple items
type RavenDBSessionConfiguration =
  inherit BasePersistableSessionConfiguration
  new : unit -> RavenDBSessionConfiguration
  new : store:IDocumentStore -> RavenDBSessionConfiguration
  new : store:IDocumentStore * cryptoConfig:CryptographyConfiguration -> RavenDBSessionConfiguration
  member Collection : string
  member Database : string
  member DocumentStore : IDocumentStore
  override Store : IPersistableSessionStore
  member Collection : string with set
  member Database : string with set
  ...

--------------------
new : unit -> RavenDBSessionConfiguration
new : store:IDocumentStore -> RavenDBSessionConfiguration
new : store:IDocumentStore * cryptoConfig:Cryptography.CryptographyConfiguration -> RavenDBSessionConfiguration
Multiple items
type HomeModule =
  inherit NancyModule
  new : unit -> HomeModule

--------------------
new : unit -> HomeModule
val this : HomeModule
Multiple items
type NancyModule =
  member After : AfterPipeline with get, set
  member Before : BeforePipeline with get, set
  member Context : NancyContext with get, set
  member Delete : path:string * action:Func<obj, obj> * ?condition:Func<NancyContext, bool> * ?name:string -> unit + 5 overloads
  member Get : path:string * action:Func<obj, obj> * ?condition:Func<NancyContext, bool> * ?name:string -> unit + 5 overloads
  member Head : path:string * action:Func<obj, obj> * ?condition:Func<NancyContext, bool> * ?name:string -> unit + 5 overloads
  member ModelBinderLocator : IModelBinderLocator with get, set
  member ModelValidationResult : ModelValidationResult with get, set
  member ModulePath : string with get, set
  member Negotiate : Negotiator
  ...

--------------------
NancyModule() : NancyModule
NancyModule(modulePath: string) : NancyModule
NancyModule.Get<'T>(path: string, action: Func<obj,Threading.CancellationToken,Threading.Tasks.Task<'T>>,?condition: Func<NancyContext,bool>,?name: string) : unit
NancyModule.Get(path: string, action: Func<obj,Threading.CancellationToken,Threading.Tasks.Task<obj>>,?condition: Func<NancyContext,bool>,?name: string) : unit
NancyModule.Get<'T>(path: string, action: Func<obj,Threading.Tasks.Task<'T>>,?condition: Func<NancyContext,bool>,?name: string) : unit
NancyModule.Get(path: string, action: Func<obj,Threading.Tasks.Task<obj>>,?condition: Func<NancyContext,bool>,?name: string) : unit
NancyModule.Get<'T>(path: string, action: Func<obj,'T>,?condition: Func<NancyContext,bool>,?name: string) : unit
NancyModule.Get(path: string, action: Func<obj,obj>,?condition: Func<NancyContext,bool>,?name: string) : unit
val count : int
property NancyModule.Request: Request
property Request.PersistableSession: IPersistableSession
abstract member IPersistableSession.Get : string -> 'T
Multiple items
type Nullable =
  static member Compare<'T> : n1:Nullable<'T> * n2:Nullable<'T> -> int
  static member Equals<'T> : n1:Nullable<'T> * n2:Nullable<'T> -> bool
  static member GetUnderlyingType : nullableType:Type -> Type

--------------------
type Nullable<'T (requires default constructor and value type and 'T :> ValueType)> =
  struct
    new : value:'T -> Nullable<'T>
    member Equals : other:obj -> bool
    member GetHashCode : unit -> int
    member GetValueOrDefault : unit -> 'T + 1 overload
    member HasValue : bool
    member ToString : unit -> string
    member Value : 'T
  end

--------------------
Nullable ()
Nullable(value: 'T) : Nullable<'T>
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
module Option

from Microsoft.FSharp.Core
val ofNullable : value:Nullable<'T> -> 'T option (requires default constructor and value type and 'T :> ValueType)
union case Option.Some: Value: 'T -> Option<'T>
val x : int
union case Option.None: Option<'T>
type obj = Object
val x : seq<string>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
val await : task:Threading.Tasks.Task<'a> -> 'a
val task : Threading.Tasks.Task<'a>
Multiple items
type Async =
  static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
  static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
  static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
  static member AwaitTask : task:Task -> Async<unit>
  static member AwaitTask : task:Task<'T> -> Async<'T>
  static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
  static member CancelDefaultToken : unit -> unit
  static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
  static member Choice : computations:seq<Async<'T option>> -> Async<'T option>
  static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  ...

--------------------
type Async<'T> =
static member Async.AwaitTask : task:Threading.Tasks.Task -> Async<unit>
static member Async.AwaitTask : task:Threading.Tasks.Task<'T> -> Async<'T>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:Threading.CancellationToken -> 'T
Multiple items
type IArticleContentConverter =
  inherit JsonConverter<IArticleContent>
  new : unit -> IArticleContentConverter
  override ReadJson : r:JsonReader * Type * IArticleContent * bool * JsonSerializer -> IArticleContent
  override WriteJson : w:JsonWriter * v:IArticleContent * JsonSerializer -> unit

--------------------
new : unit -> IArticleContentConverter
Multiple items
type JsonConverter =
  member CanConvert : objectType:Type -> bool
  member CanRead : bool
  member CanWrite : bool
  member ReadJson : reader:JsonReader * objectType:Type * existingValue:obj * serializer:JsonSerializer -> obj
  member WriteJson : writer:JsonWriter * value:obj * serializer:JsonSerializer -> unit

--------------------
type JsonConverter<'T> =
  inherit JsonConverter
  member CanConvert : objectType:Type -> bool
  member ReadJson : reader:JsonReader * objectType:Type * existingValue:obj * serializer:JsonSerializer -> obj + 1 overload
  member WriteJson : writer:JsonWriter * value:obj * serializer:JsonSerializer -> unit + 1 overload

--------------------
type JsonConverterAttribute =
  inherit Attribute
  new : converterType:Type -> JsonConverterAttribute + 1 overload
  member ConverterParameters : obj[]
  member ConverterType : Type

--------------------
JsonConverter() : JsonConverter

--------------------
JsonConverter() : JsonConverter<'T>

--------------------
JsonConverterAttribute(converterType: Type) : JsonConverterAttribute
JsonConverterAttribute(converterType: Type, [<ParamArray>] converterParameters: obj []) : JsonConverterAttribute
val w : JsonWriter
type JsonWriter =
  member AutoCompleteOnClose : bool with get, set
  member Close : unit -> unit
  member CloseAsync : ?cancellationToken:CancellationToken -> Task
  member CloseOutput : bool with get, set
  member Culture : CultureInfo with get, set
  member DateFormatHandling : DateFormatHandling with get, set
  member DateFormatString : string with get, set
  member DateTimeZoneHandling : DateTimeZoneHandling with get, set
  member FloatFormatHandling : FloatFormatHandling with get, set
  member Flush : unit -> unit
  ...
val v : IArticleContent
Multiple items
type JsonSerializer =
  new : unit -> JsonSerializer
  member Binder : SerializationBinder with get, set
  member CheckAdditionalContent : bool with get, set
  member ConstructorHandling : ConstructorHandling with get, set
  member Context : StreamingContext with get, set
  member ContractResolver : IContractResolver with get, set
  member Converters : JsonConverterCollection
  member Culture : CultureInfo with get, set
  member DateFormatHandling : DateFormatHandling with get, set
  member DateFormatString : string with get, set
  ...

--------------------
JsonSerializer() : JsonSerializer
JsonWriter.WriteStartObject() : unit
JsonWriter.WritePropertyName(name: string) : unit
JsonWriter.WritePropertyName(name: string, escape: bool) : unit
property IArticleContent.ContentType: string
JsonWriter.WriteValue(value: obj) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Uri) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: byte []) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Nullable<TimeSpan>) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Nullable<Guid>) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Nullable<DateTimeOffset>) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Nullable<DateTime>) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Nullable<decimal>) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Nullable<sbyte>) : unit
   (+0 other overloads)
JsonWriter.WriteValue(value: Nullable<byte>) : unit
   (+0 other overloads)
property IArticleContent.Text: string
JsonWriter.WriteEndObject() : unit
val __ : IArticleContentConverter
val r : JsonReader
type JsonReader =
  member Close : unit -> unit
  member CloseInput : bool with get, set
  member Culture : CultureInfo with get, set
  member DateFormatString : string with get, set
  member DateParseHandling : DateParseHandling with get, set
  member DateTimeZoneHandling : DateTimeZoneHandling with get, set
  member Depth : int
  member FloatParseHandling : FloatParseHandling with get, set
  member MaxDepth : Nullable<int> with get, set
  member Path : string
  ...
val typ : string
JsonReader.ReadAsString() : string
val text : string
JsonReader.Read() : bool
val content : IArticleContent
val x : string
val invalidOp : message:string -> 'T
module UpdatedStore

from Tres
File.ReadAllText(path: string) : string
File.ReadAllText(path: string, encoding: Text.Encoding) : string
JsonConvert.DeserializeObject<'T>(value: string) : 'T
JsonConvert.DeserializeObject(value: string) : obj
JsonConvert.DeserializeObject<'T>(value: string, settings: JsonSerializerSettings) : 'T
JsonConvert.DeserializeObject<'T>(value: string, [<ParamArray>] converters: JsonConverter []) : 'T
JsonConvert.DeserializeObject(value: string, type: Type) : obj
JsonConvert.DeserializeObject(value: string, settings: JsonSerializerSettings) : obj
JsonConvert.DeserializeObject(value: string, type: Type, settings: JsonSerializerSettings) : obj
JsonConvert.DeserializeObject(value: string, type: Type, [<ParamArray>] converters: JsonConverter []) : obj
val store : DocumentStore
Multiple items
type X509Certificate2 =
  inherit X509Certificate
  new : unit -> X509Certificate2 + 12 overloads
  member Archived : bool with get, set
  member Extensions : X509ExtensionCollection
  member FriendlyName : string with get, set
  member GetNameInfo : nameType:X509NameType * forIssuer:bool -> string
  member HasPrivateKey : bool
  member Import : rawData:byte[] -> unit + 5 overloads
  member IssuerName : X500DistinguishedName
  member NotAfter : DateTime
  member NotBefore : DateTime
  ...

--------------------
X509Certificate2() : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte []) : X509Certificate2
   (+0 other overloads)
X509Certificate2(fileName: string) : X509Certificate2
   (+0 other overloads)
X509Certificate2(handle: nativeint) : X509Certificate2
   (+0 other overloads)
X509Certificate2(certificate: X509Certificate) : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte [], password: string) : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte [], password: Security.SecureString) : X509Certificate2
   (+0 other overloads)
X509Certificate2(fileName: string, password: string) : X509Certificate2
   (+0 other overloads)
X509Certificate2(fileName: string, password: Security.SecureString) : X509Certificate2
   (+0 other overloads)
X509Certificate2(rawData: byte [], password: string, keyStorageFlags: X509KeyStorageFlags) : X509Certificate2
   (+0 other overloads)
property DocumentStoreBase.Conventions: Conventions.DocumentConventions
property Conventions.DocumentConventions.CustomizeJsonSerializer: Action<JsonSerializer>
val x : JsonSerializer
property JsonSerializer.Converters: JsonConverterCollection
Collections.ObjectModel.Collection.Add(item: JsonConverter) : unit
DocumentStore.Initialize() : IDocumentStore
Fork me on GitHub