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: |
|
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: |
|
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: |
|
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: |
|
Since F# is designed to use Option
s 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: |
|
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:
|
|
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 JsonConverter
s. 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: |
|
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: |
|
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
JsonConverter
s 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: |
|
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!
type Categories_ByWebLogIdAndSlug =
inherit AbstractJavaScriptIndexCreationTask
new : unit -> Categories_ByWebLogIdAndSlug
--------------------
new : unit -> Categories_ByWebLogIdAndSlug
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
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>
val string : value:'T -> string
--------------------
type string = System.String
type AutoOpenAttribute =
inherit Attribute
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
member Path : string
--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
type RequireQualifiedAccessAttribute =
inherit Attribute
new : unit -> RequireQualifiedAccessAttribute
--------------------
new : unit -> RequireQualifiedAccessAttribute
type LiteralAttribute =
inherit Attribute
new : unit -> LiteralAttribute
--------------------
new : unit -> LiteralAttribute
interface
abstract member Generate : unit -> string
abstract member ContentType : string
abstract member Text : string
abstract member Text : string with set
end
from Tres.Domain
type HtmlArticleContent =
interface IArticleContent
new : unit -> HtmlArticleContent
override ToString : unit -> string
--------------------
new : unit -> HtmlArticleContent
type MarkdownArticleContent =
interface IArticleContent
new : unit -> MarkdownArticleContent
override ToString : unit -> string
--------------------
new : unit -> MarkdownArticleContent
type JsonIgnoreAttribute =
inherit Attribute
new : unit -> JsonIgnoreAttribute
--------------------
JsonIgnoreAttribute() : JsonIgnoreAttribute
from Tres
type TresBootstrapper =
inherit DefaultNancyBootstrapper
new : unit -> TresBootstrapper
override ApplicationStartup : container:TinyIoCContainer * pipelines:IPipelines -> unit
override ConfigureApplicationContainer : container:TinyIoCContainer -> unit
--------------------
new : unit -> TresBootstrapper
type DefaultNancyBootstrapper =
inherit NancyBootstrapperWithRequestContainerBase<TinyIoCContainer>
new : unit -> DefaultNancyBootstrapper
member GetEnvironment : unit -> INancyEnvironment
static val DefaultAutoRegisterIgnoredAssemblies : IEnumerable<Func<Assembly, bool>>
--------------------
DefaultNancyBootstrapper() : DefaultNancyBootstrapper
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, encoding: System.Text.Encoding) : string
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(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
{Url: string;
Database: string;
Certificate: string;
Password: string;}
member Urls : string []
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
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)
(+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)
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
...
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(assemblyToScan: System.Reflection.Assembly, store: IDocumentStore,?conventions: Conventions.DocumentConventions,?database: string) : unit
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
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
type HomeModule =
inherit NancyModule
new : unit -> HomeModule
--------------------
new : unit -> HomeModule
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(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
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>
val int : value:'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
from Microsoft.FSharp.Core
val seq : sequence:seq<'T> -> seq<'T>
--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
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<'T> -> Async<'T>
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
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
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
...
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.WritePropertyName(name: string, escape: bool) : 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)
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
...
from Tres
File.ReadAllText(path: string, encoding: Text.Encoding) : string
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
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)