

.NET Framework 4.5でWCFサービスと、それを利用するクライアントアプリケーションを作っています。  
WCFサービスの引数や戻り値に使用するカスタムクラスや列挙型で、`DataMember`属性とか`EnumMember`属性を付け忘れることがあるのですが、このときにクライアント側で発生する例外のメッセージから原因を特定するのが難しく困っています。  
戻り値がカスタムクラスで、そのいずれかのプロパティで使用している`enum`のメンバーに`EnumMember`属性を付け忘れると、次のようなエラーが出ます。

Exceptionの型：`System.ServiceModel.CommunicationException`  
メッセージ：`基礎になる接続が閉じられました: 維持される必要があった接続が、サーバーによって切断されました`  
※実際には`TargetInvocationException`が発生しており、その`InnerException`に上記の`CommunicationException`が入っていた。

このメッセージから、データコントラクト周りの属性付け忘れの可能性があることまでは、経験として覚えました。  
しかし、どこで何を付け忘れているのかという具体的な原因は、今のところひたすらコードを目視で精査している状況です。  
もう少しデバッグをしやすくする手段はないものでしょうか？

例外をToStringした結果を記載しておきます。  
`System.Reflection.TargetInvocationException: 呼び出しのターゲットが例外をスローしました。 --->
System.ServiceModel.CommunicationException: 基礎になる接続が閉じられました:
維持される必要があった接続が、サーバーによって切断されました ---> System.Net.WebException: 基礎になる接続が閉じられました:
維持される必要があった接続が、サーバーによって切断されました ---> System.IO.IOException: 転送接続からデータを読み取れません:
既存の接続はリモート ホストに強制的に切断されました。。 ---> System.Net.Sockets.SocketException:
既存の接続はリモート ホストに強制的に切断されました。`  
※以下スタックトレースは省略


CC BY-SA 3.0
2015-03-30T06:36:07.060
8555
2015-04-09T11:02:52.093
2015-03-30T23:53:10.290
8078
8078
question
c# wcf
WCFの接続エラーから原因を特定する方法


`[EnumMember]`の場合`CommunicationException`ではなく内部例外のエラーメッセージを見ればフィールド名も含めて原因が分かるはずです。  
`excetion.ToString()`をログに出力するなどしておけばよいかと。


CC BY-SA 3.0
2015-03-30T10:34:04.300
8564
2015-03-30T10:34:04.300
5750
8555
answer


`wcf`のトレースログを出力するようにconfigファイルを変更し、出力されたログを下記のツールから閲覧すると、より詳しく調べることができるかもしれません。

トレースログ出力の設定方法などは以下のMSDNページに記載があります。

cf. [Service Trace Viewer Tool
(SvcTraceViewer.exe)](https://msdn.microsoft.com/en-
us/library/ms732023%28v=vs.110%29.aspx)


CC BY-SA 3.0
2015-03-31T04:16:13.127
8590
2015-03-31T04:16:13.127
3313
8555
answer


データコントラクタをチェックするコード書いてみました。  
実行時のログで調査するのも良いですが、事前に保証できるようにして置くのも  
一つの方法だと思います。  
単体テストでデータコントラクタを事前にチェックしておくのが良いと思いますが、  
私の場合は、デバッグ実行時のみチェックが走るようにしたりしています。  
ご参考まで。

[code]

    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Serialization;
    
    namespace Tests
    {
    #if DEBUG
    
        // テスト用にenumを宣言
        [DataContract]
        public enum MyEnum
        {
            [EnumMember]
            Enum1,
    
            [EnumMember]
            Enum2,
    
            // これがエラー
            Enum3,
        }
    
        // テスト用にデータコントラクタを宣言
        [DataContract]
        public class MyData
        {
            [DataMember]
            public MyEnum MyEnum { get; set; }
        }
    
        // データコントラクタをチェックするクラスです
        public static class DataContractChecker
        {
            // データコントラクタをインスタンスからチェックする
            // デバッグ実行時のチェックはこっちを使うと楽かもです。
            public static void Assert(object dataContract)
            {
                Debug.Assert(dataContract != null);
                Assert(dataContract.GetType());
            }
    
            // タイプからチェックする。
            // ユニットテストなどはこっちの方が楽かな。
            public static void Assert(Type dataContractType)
            {
                // [DataContract]属性が指定されている
                Debug.Assert(dataContractType.CustomAttributes.Any(atr => atr.AttributeType == typeof (DataContractAttribute)),
                    string.Format("class {0} に DataContract属性が必要です", dataContractType.FullName));
    
                // [DataMember]が付いているプロパティを列挙し、そのプロパティがenumのものに絞る
                var q = dataContractType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                    .Where(p => p.CustomAttributes.Any(atr => atr.AttributeType == typeof (DataMemberAttribute)))
                    .Where(p => p.PropertyType.IsEnum)
                    .Select(p => p.PropertyType);
    
                foreach (var et in q)
                {
                    // enumにも[DataContract]属性が指定されている
                    Debug.Assert(et.CustomAttributes.Any(atr => atr.AttributeType == typeof (DataContractAttribute)),
                        string.Format("enum {0} に DataContractAttribute属性が必要です", et.FullName));
    
                    foreach (var ev in et.GetEnumValues())
                    {
                        var en = ev.GetType().GetField(ev.ToString());
    
                        // enumの列挙子に[EnumMember]属性がしていされている
                        Debug.Assert(en.GetCustomAttributes(typeof (EnumMemberAttribute), false).Length == 1,
                            string.Format("enum {0} / {1} に EnumMember属性が必要です", et.FullName, en.Name));
                    }
                }
            }
    
            // 呼び出し方のサンプルです。
            public static void Test()
            {
                Assert(typeof (MyData));
                Assert(new MyData());
            }
        }
    #endif
    }
    
[/code]


CC BY-SA 3.0
2015-04-09T11:02:52.093
8953
2015-04-09T11:02:52.093
8514
8555
answer