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

は次のようなJson(とかXMLとか)を返してくれるけど、

{"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"}

まとめ


DataContractについて色々調べたのでまとめた。後で見直せばハッピーになれるかも