8

次のようなサービスメソッドを実装したいと思います。

[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat=WebMessageFormat.Json)]
public void MakeShape(string shape, string color, IDictionary<string, object> moreArgs)
{
    if (shape == "circle")
    {
        MakeCircle(color, moreArgs);
    }
}

私のクライアントは次のようなオブジェクトをPOSTします。

{
    "shape":"circle",
    "color": "blue",    
    "radius": 42,
    "filled":true,
    "annotation": {"date":"1/1/2012", "owner":"George"}
}

MakeCircleの呼び出し時に、moreArgsには3つのエントリ( "radius"、 "filled"、および2つのキーと値のペアを含む "annotation"という名前の辞書)があります。


私がこれまでに得た最高のものは次のとおりです。

//Step 1: get the raw JSON by accepting a Stream with BodyStyle=WebMessageBodyStyle.Bare
[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle=WebMessageBodyStyle.Bare)]
public void MakeShape(Stream jsonStream)
{
    //Step 2: parse it into a Dictionary with JavaScriptSerializer or JSON.net
    StreamReader reader = new StreamReader(jsonStream);
    JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
    Dictionary<string, object> args = jsSerializer.Deserialize<Dictionary<string,object>>(reader.ReadToEnd());            

    //Step 3: manually lookup and cast the "standard" arguments, and remove them from the Dictionary
    string shape = (string)args["shape"];
    string color = (string)args["color"];            

    //Step 4: make the original call, passing the remaining Dictionary as moreArgs
    MakeShape(shape,color,args);
}

ステップ3が数十のメソッド間で同期を維持するのが面倒になることを除いて、私はこのようなソリューションで生きることができました。明らかに、何かが辞書を開いて追加の引数を使用する必要がありますが、私はむしろそのコードを通信レイヤーから除外したいと思います。IMOは、引数(この場合はMakeCircleで表されます)を認識しているビジネスロジックの内部に入ります。

これらのエラーが発生しやすい手動変換を排除するため、WCFの自動バインディングが本当に気に入っています。マップする方法がわからない引数に少し余分なロジックを指定することを除いて、ほとんどすべてにそれを使用する方法があればいいのにと思います。おそらく、「[このコード]に渡して処理する」というサービス動作があるのでしょうか。


IExtensibleDataObjectによって提供される「ラウンドトリップ」サポートを検討しましたが、コードに不明なプロパティへのアクセスを許可していないようです。これらは、クライアントに送り返すことのみを目的としてラップされています。http://msdn.microsoft.com/en-us/library/ms731083.aspx


もう1つのオプションは、IDictionaryを含むカスタムクラスを使用し、どういうわけか自分で逆シリアル化を引き継ぐことです。したがって、サービスメソッドは次のようになります。[OperationContract] [WebInvoke(RequestFormat = WebMessageFormat.Json、ResponseFormat = WebMessageFormat.Json、BodyStyle = WebMessageBodyStyle.WrappedRequest)] public void MakeShape(string shape、string color、MoreArgs moreArgs)

そして、私はクライアントをより厳密な構造に強制する必要があります:

{
    "shape":"circle",
    "color": "blue",
    "moreArgs":{
        "radius": 42,
        "filled":true
        "annotation": {"date":"1/1/2012", "owner":"George"}
        }
}

それは理想的ではありませんが、私はそれと一緒に暮らすことができました。問題は、MoreArgsを定義し、適切に設定する方法です。私の次の試み:

[DataContract]
public class MoreArgs : ISerializable
{
    public Dictionary<string, object> Properties;
    public MoreArgs(SerializationInfo info, StreamingContext context)  
    {
        Properties = new Dictionary<string, object>();  
        foreach (var entry in info)  
        {                      
        Properties.Add(entry.Name, entry.Value);  
        }
    }  
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        foreach (string key in Properties.Keys)
        {
        info.AddValue(key, Properties[key]);
        }  
    }
}

これにより、サービスの開始時にInvalidDataContractExceptionがスローされます(... MoreArgsはISerializableにすることはできず、DataContractAttribute属性を持つことはできません)。

[DataContract]属性を削除すると、私が期待するInvalidDataContractExceptionがスローされます(... MoreArgs'はシリアル化できません。DataContractAttribute属性でマークすることを検討してください...)。

また、予想どおり、ISerializable継承を削除すると例外がクリアされますが、MakeCircleの呼び出しでmoreArgs.Propertiesがnullになります。


また、使用できるハイブリッドソリューションがあるかどうかも疑問に思います。多分:

  • 私の最初の試みのように、ストリームを受け入れて引数辞書を作成します
  • 後の試みのように、MoreArgs引数を使用してメソッドを定義します
  • ストリームから取得したディクショナリからMoreArgsオブジェクトを入力します
  • どういうわけか、「これらの引数がある場合に呼び出されるメソッドを呼び出す」と言って、WCFを再度呼び出します(元の引数ディクショナリと、適切に入力された新しいMoreArgsを指定します)。

MoreArgsには元の引数も含まれますが、それはおそらく災害ではありません。リフレクションを使用して必要な呼び出しを行うことはおそらく可能だと思いますが、WCFがこの関数を内部でデバッグし、起動するように最適化する必要がある場合、それはばかげていると感じます。

4

1 に答える 1

3

@メリッサエイブリーウィアー

私は自分の「解決策」に満足していませんが、前進しなければなりませんでした。

アプリの起動時に、呼び出したいすべてのメソッドについて、ルックアップテーブルにMethodInfoを詰め込みます。これは、インターフェースとメソッド名によってキー設定されています。私はそれらを見つけるためにリフレクションとカスタム属性を使用しますが、ここではいくつものテクニックが機能する可能性があります。

私の1つのWCFサービスメソッドは、インターフェイス名、メソッド名、およびStreamを引数として受け入れます。JSON.NETを使用して、引数をディクショナリに逆シリアル化し、それをディスパッチャに渡します。

ディスパッチャは、インターフェイスとメソッド名でMethodInfoを検索します。次に、辞書の引数をMethodInfoのパラメーターに一致させて、引数の配列を入力します。ターゲットメソッドに実際にDictionarymoreArgsパラメータがある場合、一致しない引数を取得します。最後に、MethodInfo.Invokeを呼び出して、新しく入力された引数配列を渡します。

WCFが私のためにほとんど行うことを行うのは面倒なコードですが、より良い解決策は見つかりませんでした。

これらすべてを自分で制御することにはいくつかの利点があります。私のお気に入りは、保存されたMethodInfosを使用して、必要な言語でクライアント側の呼び出しスタブを自動的に生成する機能です。

遅延バインディングがパフォーマンスの問題であることが判明した場合は、すべての呼び出しを手動で大きなスイッチ(methodName)に配置することを検討します。それでもインターフェイスが頻繁に変更される場合は、ビルドステップとしてその定型的なバインディングコードを生成しようとする場合があります。DBアクセスでボトルネックになるので、おそらく気にしないでしょう。

于 2013-01-09T00:13:04.873 に答える