我想实现这样的服务方法:

[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”和一个名为“annotation”的字典,其中包含2个键值对。)

到目前为止,我得到的最好成绩是:
//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的自动绑定(bind),因为它消除了这些容易出错的手动翻译。我希望有一种方法可以用于几乎所有内容,除了为它不知道如何映射的参数指定一些额外的逻辑。也许有某种服务行为表明“将它们传递给[此代码],然后我将处理”?

我已经考虑过IExtensibleDataObject提供的“往返”支持,但是它似乎并不能使我的代码访问未知属性-它们被包装起来只是为了发回给客户端。 http://msdn.microsoft.com/en-us/library/ms731083.aspx

另一种选择是使用包含IDictionary的自定义类,然后以某种方式自己接管反序列化。因此,服务方法为:
[运营契约(Contract)]
[WebInvoke(RequestFormat = WebMessageFormat.Json,ResponseFormat = WebMessageFormat.Json,BodyStyle = WebMessageBodyStyle.WrappedRequest)]
公共(public)无效MakeShape(字符串形状,字符串颜色,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为空。

我还想知道是否可以使用某些混合解决方案?也许:
  • 像我第一次尝试时那样接受流并构建参数字典
  • 使用MoreArgs参数定义方法,例如我稍后的尝试
  • 从从流
  • 流中提取的字典中填充MoreArgs对象
  • 以某种方式重新调用WCF,说“调用具有这些参数时将被调用的方法”(指定原始参数字典,以及新的正确填充的MoreArgs)。

  • 然后,MoreArgs也将包含原始参数,但这可能不是灾难。我想我可能可以使用反射进行调用,但是当WCF必须在内部具有此功能,进行调试和优化以进行启动时,这感觉很愚蠢。

    最佳答案

    @梅丽莎·艾弗里·威尔

    我对自己的“解决方案”不满意,但我必须继续前进。

    在应用程序启动时,对于我要调用的每个方法,我都会在查询表中填充一个MethodInfo。它由接口(interface)和方法名称键控。我使用反射和自定义属性来找到它们,但是许多技术都可以在这里工作。

    我的一个WCF服务方法接受一个接口(interface)名称,一个方法名称和一个Stream作为参数。我使用JSON.NET将参数反序列化为Dictionary,并将其传递给调度程序。

    调度程序通过接口(interface)和方法名称查找MethodInfo。然后,将字典中的参数与MethodInfo中的参数进行匹配,我将填充一个参数数组。如果目标方法实际上具有Dictionary moreArgs参数,则它将获取所有不匹配的参数。最后,我调用MethodInfo.Invoke,并传递新填充的参数数组。

    做很多WCF几乎可以为我做的事情的代码很繁琐,但是我没有找到更好的解决方案。

    自己控制所有这些都有一些好处。我最喜欢的功能是能够使用保存的MethodInfos以我想要的任何语言自动生成客户端调用 stub 。

    如果发现后期绑定(bind)是性能问题,我将考虑将所有调用手动放在一个大的switch(methodName)中。如果我的界面仍然经常更改,则可以尝试生成该样板绑定(bind)代码作为构建步骤。也许我永远都不会打扰,因为我将成为数据库访问的瓶颈。

    10-06 01:06