2010-03-05 93 views
4

我想用WCF服务替换一个asmx WebService。我的主要目标是保持SOAP消息相同。调用者不是.NET,需要大量的重新工作才能对合同进行微小的更改。可以将单个数组作为参数的WCF操作使用MessageContracts吗?

我的痛点是,Web方法,我试图取代的webmethod使用下列属性减速:

[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)] 

这消除了围绕每一个参数的XML元素的一个额外层。

我知道使用WCF模拟此的唯一方法是使用MessageContracts而不是DataContracts,并使用WrappedName和IsWrapped属性来控制参数如何格式化。

这种方法适用于我所有的方法,除了一个,它将一个POCO对象的单个数组作为参数。

我的结论是,我没有选择。我无法升级这个webservice并维护同一个合同。

我的问题是:

1)是复制的唯一途径:

[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)] 

在WCF Web方法使用MessageContract?

2)有没有办法让一个方法把一个数组作为参数?

下面是一个简单实施例I一直在努力,以显示我所看到/用做 [SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]和消息合同

背衬服务代码:

using System.Web.Services; 
using System.Web.Services.Protocols; 
using System.ServiceModel; 
namespace WebApplication1{ 

    /// <summary> 
    /// The Service Contract 
    /// </summary> 
    [ServiceContract] 
    public interface ISimpleMathService 
    { 
     [OperationContract()] 
     AddResp Add(AddReq add); 
    } 
    /// <summary> 
    /// The Service Implementation 
    /// </summary> 
    public class simpleMath : ISimpleMathService 
    { 
     [WebMethod()] //Allows the Service to be exposed as a asmx Service 
     [SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)] 
     public AddResp Add(AddReq add) 
     { 
      return new AddResp {result = add.NumOne + add.NumTwo}; 
     } 
    } 
} 

POCO对象:(V1与数据协定)

using System.Runtime.Serialization; 
using System.ServiceModel; 

namespace WebApplication1 
{ 
    [DataContract] 
    public class AddReq 
    { 

     [DataMember] 
     public int NumOne { get; set; } 

     [DataMember] 
     public int NumTwo { get; set; } 
    } 



    [DataContract] 
    public class AddResp 
    { 

     [DataMember] 

     public int result{ get; set; } 


    } 
} 

ASMX SOAP

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"> 
    <soapenv:Header/> 
    <soapenv:Body> 
     <tem:add> 
     <tem:NumOne>12</tem:NumOne> 
     <tem:NumTwo>12</tem:NumTwo> 
     </tem:add> 
    </soapenv:Body> 
</soapenv:Envelope> 

SOAP请求,与WCF数据契约

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:web="http://schemas.datacontract.org/2004/07/WebApplication1"> 
    <soap:Header/> 
    <soap:Body> 
     <tem:Add> 
     <tem:add> 
      <web:NumOne>10</web:NumOne> 
      <web:NumTwo>10</web:NumTwo> 
     </tem:add> 
     </tem:Add> 
    </soap:Body> 
</soap:Envelope> 

让我们的参数使用消息合同并返回类型: POCO对象:(V2与MessageContracts)

namespace WebApplication1 
{ 
    [DataContract] 
    [MessageContract(WrapperName="add", IsWrapped = true)] //Default Wrapper Name is "Add", not add 
    public class AddReq 
    { 

     [DataMember] 
     [MessageBodyMember] 
     public int NumOne { get; set; } 

     [DataMember] 
     [MessageBodyMember] 
     public int NumTwo { get; set; } 
    } 



    [DataContract] 
    [MessageContract(IsWrapped = true)] 
    public class AddResp 
    { 

     [DataMember] 
     [MessageBodyMember] 
     public int result{ get; set; } 


    } 
} 

WCF SOAP请求(V2):

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/"> 
    <soap:Header/> 
    <soap:Body> 
     <tem:add> 
     <tem:NumOne>19</tem:NumOne> 
     <tem:NumTwo>12</tem:NumTwo> 
     </tem:add> 
    </soap:Body> 
</soap:Envelope> 

这就是我现在正在做的事,它符合我需要的90%。

的问题是,我想实现这样的WCFand的方法守合同相同:

[WebMethod()] 
[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)] 
public AddResp AddArrays(AddReq [] addInput) 
{ 
    AddResp resp= new AddResp{result=0} 
    foreach (var addrequest in addInput) 
    { 
     resp.result += (addrequest.NumOne + addrequest.NumTwo); 
    } 
    return resp; 
} 

当我做到这一点,现在,我得到下面的异常,因为AddReq []是不是MessageContract。 AddReq []是System.Array类型的,我不能改变它。

无法加载操作'AddArrays',因为它具有System.ServiceModel.Channels.Message类型的参数或返回类型或具有MessageContractAttribute和其他不同类型参数的类型。当使用System.ServiceModel.Channels.Message或具有MessageContractAttribute的类型时,该方法不得使用任何其他类型的参数。

感谢, 布赖恩

回答

2

事实证明,你可以添加一个“主机类”与IsWrapped = false,它的工作原理。

从原来的问题样本,这是包装类会是什么样子:

[DataContract,MessageContract(IsWrapped=false)] 
public class AddArraysReq 
{ 
    [DataMember] 
    [MessageBodyMember] 
    public AddReq[] AddReqs; 

} 

这是方法会是什么样子:

public AddResp AddArrays(AddArraysReq addInput) 
    { 
     AddResp resp = new AddResp {result = 0}; 
     foreach (var addrequest in addInput.AddReqs) 
     { 
      resp.result += (addrequest.NumOne + addrequest.NumTwo); 
     } 
     return resp; 
    } 

产生的SOAP请求:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
    xmlns:tem="http://tempuri.org/" 
    xmlns:web="http://schemas.datacontract.org/2004/07/WebApplication1"> 
    <soap:Header/> 
    <soap:Body> 
     <tem:AddReqs>   
     <web:AddReq> 
      <web:NumOne>10</web:NumOne> 
      <web:NumTwo>10</web:NumTwo> 
     </web:AddReq> 
     <web:AddReq> 
      <web:NumOne>10</web:NumOne> 
      <web:NumTwo>10</web:NumTwo> 
     </web:AddReq> 
     </tem:AddReqs> 
    </soap:Body> 
</soap:Envelope> 

我没有意识到IsWrapped = false删除了所有类的代表来自请求。

1

我有点被你的声明难倒,那SoapParameterStyle.Bare消除周围的参数XML层,但你可以只复制使用消息合同,IsWrapped=true,而这又主要增加了围绕SOAP有效载荷的XML包装......似乎有点矛盾。

你能向我们展示你的方法的Web方法声明,该方法以POCO数组为参数?到目前为止,你在WCF中尝试过什么?通常,与BasicHttpBinding和几乎没有选项,你很接近ASMX在过去的做法。

+0

Marc, 我很感谢您的回复。 我已经添加了关于SoapParameterStyle.Bare的通用代码示例,以及我如何使用消息合约来替换asmx Soap格式。 我的意图不是说我必须使用IsWrapped = true,而是使用属性属性来获得预期的效果。我希望我的样品能澄清我的意思。 在这种情况下,“非常接近”并不够接近。我可能能够处理元素名称的变化,但是如果元素嵌套发生变化,我就会遇到问题。 – 2010-03-05 20:37:04

相关问题