WCFで通信エラーになる場合

c#とWCFでプロセス間通信をするためにNetTcpBindingを使って通信を行っていたら以下のメッセージが出力され通信エラーとなりました。

System.ServiceModel.CommunicationException: ソケット接続が中止されました。これは、メッセージ処理時のエラー、リモート ホストでの受信タイムアウトの超過、または基になるネットワーク リソースの問題が原因で発生する可能性があります。ローカル ソケットのタイムアウトは '00:01:00' でした。 ---> System.Net.Sockets.SocketException: 既存の接続はリモート ホストに強制的に切断されました。

で、この原因なんですが

DataContractSerializer.MaxItemsInObjectGraph

の制限に引っかかって通信が切断されたのが原因でした。

ただ、MSDNの説明に

プロパティ値
Type: System.Int32
シリアル化または逆シリアル化する項目の最大数。既定値は、MaxValue です。

とあり、最大値が「2,147, 483,647」(21億)と記述があるのですが、この記述のままではなく、設定しないと(たぶん)65,535となっています。このため、リストにオブジェクトを入れて送信すると割とすぐにエラーが発生します。

修正方法

定義ファイルをツールから修正する

定義ファイルの場合、WCFサービス構成エディタで定義ファイルを開いてツリーから

詳細設定 > サービス動作 > MyBehavior を選択し(無い場合作成して)

画面中央の[追加]を指定して

dataContractSerializerを追加し、設定値の、MaxItemsInObjectGraphへ任意の大きい隊を入力します。

定義ファイルを手で修正する

app.config内のビヘイビアへ以下を追記します。

</system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="MyBehavior">
        <dataContractSerializer maxItemsInObjectGraph="1073741824" /> ★これを追加
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

behaviorノード自体が無いときはノードごと追加します。
この場合、bindingへノードの参照を追加しないといけないのですがここでは割愛します。(というかツール使ってください)

c#コード上で修正する

ServiceHostを宣言している箇所で以下コードを記述します。

// サービスの宣言(適当)
var service = new ServiceHost(myType);

// ビヘイビアの構成定義に当たるオブジェクトを取得
var config = this.service.Description.Behaviors.Find<ServiceBehaviorAttribute>();

// 取れない場合新規追加
if (config == null)
{
    config = new ServiceBehaviorAttribute()
    {
      MaxItemsInObjectGraph = int.MaxValue
    }
}

// 任意の大きな値を設定
serviceBehaviorAttribute.MaxItemsInObjectGraph = int.MaxValue;

さいごに

ってかオブジェクトの初期値がint.MaxValueってだけでWCFはあとから値を上書きしてるっぽいですね。
MSDN見て安心してたけどこういう事もあるんですね。テスト重要。