如何手动创建WCF OperationContract体型?

时间:2009-03-12 07:02:54

标签: .net wcf web-services serialization rest

我正在尝试在Silverlight中为我开发的RESTful WCF服务编写Web服务客户端代码。在Silverlight中,我使用DataContractSerializer实例构建WebRequest的主体。

如果OperationContract有一个参数,这种方法很有用。如果OperationContract中定义了多个参数,则它不能很好地工作。我相信这是因为WCF创建了一个以OperationContract命名的动态类型,并且该类型的成员以为该操作定义的参数命名。动态类型的目的是确保在提交给WCF服务的消息体中存在单个XML元素......这是有意义的。问题是我如何自己构建这个动态类型,以便我可以自己将它提交给DataContractSerializer。

第一个示例是定义单个参数的工作示例。第二个例子是我想要解决的场景(多个参数)。


示例1:

[OperationContract,
WebInvoke(Method = HttpMethodType.Post,
    BodyStyle = WebMessageBodyStyle.Bare,
    UriTemplate = "UnregisterProvider"),
WebHelp(Comment = "Unregistered the provider type with the specified URI.")]
void UnregisterProvider(RdfUri providerUri);

用于序列化邮件正文的代码:

StringBuilder msgBody = new StringBuilder(250);
using (XmlWriter xw = XmlWriter.Create(msgBody))
{
    var serializer = new DataContractSerializer(typeof(RdfUri));
    serializer.WriteObject(xw, providerUri);
}

结果身体:

<RdfUri xmlns="http://schemas.datacontract.org/2004/07/Intellidimension.Rdf">esp:semanticserver</RdfUri>

示例2:

[OperationContract,
WebInvoke(Method = HttpMethodType.Post,
    BodyStyle = WebMessageBodyStyle.WrappedRequest,  /* WrappedRequest must somehow signal WCF to create the anonymous type as it is required for multiple parameter OperationContracts */
    UriTemplate = "RegisterProvider"),
WebHelp(Comment = "Registered a provider type with the specified URI.")]
void RegisterProvider(PoolableEntityServiceProviderDescriptor descriptor, RdfUri providerUri);

用于序列化邮件正文的代码:

//?????

结果身体:

<RegisterProvider xmlns="http://tempuri.org/">
  <descriptor i:type="a:SemanticServerProviderDescriptor" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:a="http://schemas.datacontract.org/2004/07/Intellidimension.RdfEntity.Service.DataContracts">
    <a:ConnectionString>Data Source=.\sqlexpress;Initial Catalog=RdfTest1;Persist Security Info=True;User ID=sa;Password=password</a:ConnectionString>
    <a:ProviderGraphUri>http://entitystore/graph-provider</a:ProviderGraphUri>
  </descriptor>
  <providerUri>esp:semanticserver</providerUri>
</RegisterProvider>

更新1

以下是MSDN论坛上的一位提问类似问题的人:Can I use DataContractSerializerOperationFormatter to format a list of parameters from client to server?

DataContractSerializerOperationFormatter是一个内部类。所以看起来我可能必须为我的客户实现它的行为。

更新2

有些人在问我为什么不只是使用服务引用生成的普通Silverlight WCF客户端。原因是服务器上的WCF服务是RESTful服务。来自docs

  

没有提供WCF中提供的WebHttpBinding的模拟信息。要从Silverlight 2访问纯HTTP,REST,RSS / Atom或AJAX服务,请使用直接访问基于HTTP的服务中描述的技术,例如WebClient类。要访问ASP.NET AJAX服务,请参阅访问ASP.NET AJAX服务。

2 个答案:

答案 0 :(得分:1)

我实际上没有尝试过这个,但this article可能有你想要的。他通过这样做创建了一条包装好的消息:

    private void ConsumeWcfRest()
    {
        string url = "http://something/MyWebsite/tagservice";

        Tag tag1 = new Tag() { ID = 1, TagName = "test1" };
        Tag tag2 = new Tag() { ID = 2, TagName = "test2" };

        HttpWebRequest req = HttpWebRequest.Create(url + "/tags/wrapped/1") as HttpWebRequest;
        req.Method = "POST";
        string content = "<DoSomethingWrapped xmlns='http://mytagservice'>";
        content = content + DoSerialize(tag1, "tag1");
        content = content + DoSerialize(tag2, "tag2");
        content = content + "</DoSomethingWrapped>";

        // .....
    }

    private string DoSerialize(object obj, string rootName)
    {
        System.Runtime.Serialization.DataContractSerializer se;
        if (string.IsNullOrEmpty(rootName))
            se = new System.Runtime.Serialization.DataContractSerializer(obj.GetType());
        else
            se = new System.Runtime.Serialization.DataContractSerializer(obj.GetType(), rootName, "");

        System.IO.MemoryStream ms = new System.IO.MemoryStream();
        se.WriteObject(ms, obj);
        ms.Position = 0;
        byte[] arr = new byte[ms.Length];
        ms.Read(arr, 0, Convert.ToInt32(ms.Length));
        return new System.Text.UTF8Encoding().GetString(arr);
    }

所以基本上他使用普通的DataContractSerializer来序列化每个参数,然后手动将XML标签放在那里。这看起来有点像hacky,但它可能有用......

您可能想要开始反编译WCF库,看看您是否可以首先了解WCF是如何做到的。

答案 1 :(得分:0)

让我问你一个问题。让我们说你可以让这个工作。 POST请求正文的内容类型是什么?

您试图假装POST可以发送两件事,从而滥用HTTP。如果你想做你正在做的事情,那么为什么不使用常规的WCF服务合同。用这些参数发送多个参数很容易。

System.ServiceModel.Web的问题在于它只有90%将客户端与WCF分离。因此,要使事情真正正常工作,您需要在客户端进行WCF。此时为何不坚持常规的WCF服务合同。

如果你真的想按照预期使用HTTP,那么使用一种有助于而不是阻碍的技术。