F#+WCFでRESTfulなサービスをこしらえる・DataContractについて
WCFを構成する4つのContract
WCFに登場する用語にコントラクト(Contract)というものがあります。
コントラクトとは、「サービスがどのような機能を提供するか」を定義するためのものです。
また、コントラクトはサービスがクライアントに対して公開するインターフェースとしての役割も持ちます。
コントラクトは、抽象度が高い順番に、
- ServiceContract
- OperationContrace
- MessageContract
- DataContract
と言う名前を持っています。
DataContract
データコントラクト(DataContract)は、メッセージコントラクト(MessageContract)を構成する要素なのですが、
MessageContractが僕はまだ分かっていないので、「WCFがクライアントとやり取りするデータ単位」だと思っています。
(違っていたら教えてください)
基本的にはここに全部載っているはずです データ コントラクトの使用 | Microsoft Docs
WCFでデータコントラクトとして使用できるのは、.NET Frameworkがシリアライズ出来る型と、DataContractAttributeが付与されたクラスだけです。
stringやintは.NETがシリアライズしてくれるので、WCFでデータコントラクトとして使用可能です。
昨日の例で言えば、
[<DataContract>] type MyData(n:string) = let mutable name = n [<DataMember(Name="NAME")>] member this.Name with get() = name and set(value) = name <- value [<ServiceContract>] type IGetDataService = interface ... [<WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate="temp3/{value}")>] [<OperationContract>] abstract GetData3 : value:string -> MyData
最後の行の string -> MyData という関数の引数と戻り値それぞれが、データコントラクトです。
stringはシリアライズ可能型、MyDataは事前に定義してあるDataContractAttributeを付与したカスタム型です。
DataContractAttributeについて
んで、今日はこのDataContractAttributeの備忘録です。
DataContractAttributeについて
- Name プロパティはデータの名前をシリアライズ/デシリアライズするためのもの。これはXMLの場合はシリアライズされるけど、Jsonの場合はデータ全体の名前に相当する部分がないので意味ないと思う
- Namespace プロパティも大事。名前のバッティングを避けるためだけど、今までXMLの名前の競合に遭遇したことないから実はどれだけ大事なのかよくわからないでいる。とりあえず付けておくと吉かな。URIを指定する事が多いけど別にURIじゃなくてもいい。
- IsReference プロパティはよくわからない 相互運用可能なオブジェクト参照 | Microsoft Docs
こういうデータを定義したら、
[<DataContract(Name="MYDATA", Namespace="http://namespace")>] type MyData(n:string) = let mutable name = n let mutable num = 100; [<DataMember(Name="NAME")>] member this.Name with get() = name and set(value) = name <- value [<DataMember(Name="NUM")>] member this.Num with get() = num and set(value) = num <- value
こんなXMLが帰ってくる
<MYDATA> <NAME>hoge</NAME> <NUM>100</NUM> </MYDATA>
DataMember
- XMLシリアライザ(System.Xml.Serialization)みたいにSystem.Xml.Serialization.XmlIgnoreAttributeに相当する属性を付けなくても、DataMemberを指定しないデータは勝手に無視される、と思う
- Name プロパティはシリアライズ/デシリアライズ時の名前。付けないとプロパティ名が勝手に付く
- DataMember はいわゆるpublicなプロパティである事が必要なので、F#で書くと、
let mutable name = n [<DataMember(Name="NAME")>] member this.Name with get() = name and set(value) = name <- value
とmutableを使用した上に、あまつさえC#では1行で記述出来たりとちょっともやっとする。
かといって
let name = n [<DataMember(Name="NAME")>] member this.Name with get() = name and set(value) = ()
こういう書き方も出来るには出来るけど、これが良いか悪いかは判断しかねる。個人的にはsetプロパティが何もしないのにオープンなのはどうかなと思う。
- Order プロパティは要素の順序を指定するもの。IsRequire プロパティは必須か否かを指定するもの。IsRequire はデータリクエスト時に効果を発揮しそう
例えば、
[<DataContract(Name="MYDATA")>] type MyData(n:string) = let mutable name = n let mutable num = 100; [<DataMember(Name="NAME")>] member this.Name with get() = name and set(value) = name <- value [<DataMember(Name="NUM")>] member this.Num with get() = num and set(value) = num <- value
{"NAME":"hoge","NUM":100}
次のように書くと
[<DataContract(Name="MYDATA")>] type MyData(n:string) = let mutable name = n let mutable num = 100; [<DataMember(Name="NAME", Order=1)>] member this.Name with get() = name and set(value) = name <- value [<DataMember(Name="NUM", Order=0)>] member this.Num with get() = num and set(value) = num <- value
順序が適用される。
{"NUM":100,"NAME":"hoge"}
- EmitDefaultValue プロパティは何か難しいので、ここを読む データ メンバーの既定値 | Microsoft Docs
まとめ
DataContractについて色々調べたのでまとめた。後で見直せばハッピーになれるかも