将单个数组作为参数的WCF操作可以使用MessageContracts吗?

时间:2010-03-05 19:49:46

标签: .net wcf soap messagecontract

我正在尝试用WCF服务替换asmx WebService。我的主要目标是保持SOAP消息相同。调用者不是.NET,并且需要进行大量的重新工作才能对合同进行微小的更改。

我的痛点是我试图替换webmethod的web方法使用以下属性减速:

[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]

这将删除每个参数周围的额外XML元素层。

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

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

我的结论是我没有选择权。我无法升级此Web服务并维护相同的合同。

我的问题是:

1)是复制的唯一方法:

    [SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]

在WCF中的Web方法上使用MessageContract?

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

这是一个简单的示例,我一直在努力展示我所看到/正在做的事情  [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对象:(带MessageContracts的V2)

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肥皂请求(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%。

问题是,我想在WCF中实现这样的方法并保持合同相同:

[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 个答案:

答案 0 :(得分: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 :(得分:1)

我对你的陈述感到有点困惑,SoapParameterStyle.Bare在参数周围删除了一层XML,但你只能通过使用IsWrapped=true的消息契约来复制它,而BasicHttpBinding基本上< em>再次围绕SOAP有效负载添加 XML包装器......似乎有点矛盾。

您能告诉我们您的方法的Web方法声明,它将POCO数组作为参数吗?你到目前为止在WCF中尝试过什么?通常情况下,使用{{1}}并且几乎没有选项,您可以非常接近ASMX过去所做的事情。