7

以下を更新...

最近、F#でServiceStackの実験を開始したので、当然、HelloWorldサンプルの移植から始めました。

open ServiceStack.ServiceHost
open ServiceStack.ServiceInterface
open ServiceStack.WebHost.Endpoints

[<CLIMutable; Route("/hello"); Route("/hello/{Name}")>]
type Hello = { Name : string }

[<CLIMutable>]
type HelloResponse = { Result : string }

type HelloService() =
    inherit Service()

    member x.Any(req:Hello) =
        box { Result = sprintf "Hello, %s!" req.Name }

type HelloAppHost() =
    inherit AppHostBase("Hello Web Services", typeof<HelloService>.Assembly)
    override x.Configure container = ()

type Global() =
    inherit System.Web.HttpApplication()

    member x.Application_Start() =
        let appHost = new HelloAppHost()
        appHost.Init()

それはうまくいきます。それは非常に簡潔で、扱いやすく、私はそれが大好きです。ただし、サンプルで定義されているルートでは、Nameパラメーターを含めることができないことに気付きました。もちろん、Hello, !出力としてはちょっと足りないように見えます。を使用することもできますが、F#では、 OptionタイプString.IsNullOrEmptyを使用してオプションであるものについて明示するのは慣用句です。そこで、それに応じてタイプを変更して、何が起こるかを確認しました。Hello

[<CLIMutable; Route("/hello"); Route("/hello/{Name}")>]
type Hello = { Name : string option }

これを行うとすぐに、F#型システムは値を持たない可能性があるという事実に対処することを余儀なくされたので、すべてをコンパイルするためにこれNameに変更しました。HelloService

type HelloService() =
    inherit Service()

    member x.Any(req:Hello) =
        box { Result = 
                match req.Name with
                | Some name -> sprintf "Hello, %s!" name
                | None -> "Hello!" }

これはコンパイルされ、パラメーターを指定しない場合は完全に実行されNameます。しかし、私が名前を提供するとき...

KeyValueDataContractDeserializer:タイプへの変換エラー:タイプ定義は「{」で始まる必要があります。シリアル化されたタイプ「FSharpOption`1」が必要です。次で始まる文字列を取得します:World

もちろん、これは完全な驚きではありませんでしたが、それは私の質問に私をもたらします:

Tタイプのインスタンスをタイプのインスタンスにラップできる関数を作成するのは簡単ですFSharpOption<T>逆シリアル化中に使用するためにそのような関数を提供できるようにするフックはServiceStackにありますか?見ましたが、何も見つかりませんでした。間違った場所を探していたのではないかと思います。

F#で定義されたクラスはデフォルトでnullに許可されていないため、これはF#の使用にとって最初に思われるよりも重要です。したがって、あるクラスを別のクラスのオプションのプロパティとして持つ唯一の(満足のいく、ハッキーではない)方法は、ご想像のとおり、Option型を使用することです。


アップデート:

次の変更を加えることで、これを機能させることができました。

ServiceStackソースで、このタイプを公開しました。 ServiceStack.Text.Common.ParseFactoryDelegate

...そして私もこのフィールドを公開しました: ServiceStack.Text.Jsv.JsvReader.ParseFnCache

ParseFnCacheこれらの2つが公開されたので、辞書を変更するためにこのコードをF#で記述できました。AppHostのインスタンスを作成する前に、このコードを実行する必要がありました。AppHostのConfigureメソッド内で実行した場合、このコードは機能しませんでした。

JsvReader.ParseFnCache.[typeof<Option<string>>] <- 
    ParseFactoryDelegate(fun () -> 
        ParseStringDelegate(fun s -> (if String.IsNullOrEmpty s then None else Some s) |> box))

これは私の元のテストケースでは機能しますが、ServiceStackの内部に脆弱な変更を加える必要があったことを除けば、ラップできるようにしたいタイプごとに1回行う必要があるため、問題がありOption<T>ます。

これを一般的な方法で実行できれば、もっと良いでしょう。C#の用語では、ServiceStackに提供できれば素晴らしいでしょうFunc<T, Option<T>>。ServiceStackは、ジェネリック型の定義が関数の戻り型の定義と一致するプロパティを逆シリアル化するときに、逆シリアルT化してから結果を関数に渡します。

そのようなものは驚くほど便利ですが、それが実際にServiceStackの一部であり、おそらく他の場所で何かを壊すような醜いハックではない場合は、ラップごとに1回のアプローチで生きることができます。

4

2 に答える 2

4

そのため、ServiceStack にはいくつかの拡張ポイントがあります。フレームワーク レベルでは、独自のカスタム リクエスト バインダーを追加できます。これにより、使用される独自のモデル バインダーを提供できます。

base.RequestBinders.Add(typeof(Hello), httpReq => {
    var requestDto = ...;
    return requestDto;
});

ただし、さまざまな Content-Types のモデル バインディングを自分で処理する必要があります。ServiceStack がそれを行う方法については、 CreateContentTypeRequestを参照してください。

次に、JSON シリアライザー レベルにフックがあります。

JsConfig<Hello>.OnDeserializedFn = dto => newDto;

これにより、返された型のインスタンスを変更できますが、同じ型である必要がありますが、F# オプション修飾子が型の構造定義を変更しているように見えますか?

しかし、ServiceStack を F# でより使いやすくするためのフックを追加することにはオープンです。通常の型をオプション付きHelloの F#型に一般的に変換するコードはどのようなものですか?Hello

于 2012-10-11T11:07:18.847 に答える
1

私が考えることができる唯一のことは、オプションタイプを独自のタイプ、からstringへの暗黙の変換を持つタイプmyOption、およびその他の必要なものに置き換えることです。

それほど素晴らしいわけではありませんが、実行可能です。あなたのタイプもおそらくシリアライズ可能である必要があります。

type myOption = 
   | None 
   | Some of string
   static  member public op_Implicit (s:string) = if s <> null then Some s else None
   member public this.Value = match this with 
                              | Some s -> s
                              | _      -> null
   member this.Opt = match this with 
                     | Some s -> Option.Some s
                     | None   -> Option.None 

その場合、レコードタイプは次のようになります

[<CLIMutable>]
type Hello = 
   { Name : myOption }

一方、ServiceStackオープンソースであるため、そこで何かが行われる可能性があります。

于 2012-10-11T10:53:01.023 に答える