如何使用基于第三方WSDL的消息合同正确生成服务引用,或者在WF服务项目中强制不签署消息合同

时间:2013-11-08 22:05:51

标签: c# web-services visual-studio-2012 wsdl workflow-foundation-4

我遇到一个问题,即第三方WSDL我可以从Console App轻松创建一个有效的服务代理,但是从WF4 WF服务我不是。在后一种情况下生成的代理显然是错误的,具体涉及2个问题: a)在未请求或不需要时始终生成消息合同 b)使用了错误的响应消息和xml包装器名称,导致空响应对象和反序列化失败

我面临的问题是在第三方WSDL的基础上实际生成Reference.cs类。在WSDL中有许多操作,按照出现的顺序,其中2个如下:

 <operation name="pu013">
      <documentation>
        <description>Check-response service</description>
        <help>The service handles (cut out)</help>
      </documentation>
      <input message="tns:pu013Request" />
      <output message="tns:SimpleResponse" />
 </operation>

...
 <operation name="mi102">
      <documentation>
        <description>Instruction insert to Matching System</description>
        <help>This service (cut out)</help>
      </documentation>
      <input message="tns:mi102Request" />
      <output message="tns:SimpleResponse" />
    </operation> 

Reference.cs中的结果是以下C#:

WorkflowService1.PSE.pu013Response pu013(WorkflowService1.PSE.pu013Request request);

...

WorkflowService1.PSE.pu013Response mi102(WorkflowService1.PSE.mi102Request request); 

请注意,由于某种原因,使用pu013Response的INCORRECT响应消息生成mi102操作,该消息声明为:

 [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.MessageContractAttribute(WrapperName="pu013Response", WrapperNamespace="http://pse/", IsWrapped=true)]
    public partial class pu013Response { 

注意WrapperName阻止XML序列化程序识别响应,即mi102Response,因此对于非pu013的所有操作,我总是得到NULL响应。

此外,如果我从控制台应用程序添加引用,则不会发生这种情况。这不会生成Message契约,在这种情况下,调用和响应也会生效。

有什么不同? svcutil是否在幕后调用?如果是这样,使用的参数有什么不同?是否可以使用svcutil来生成xamlx活动,以便找到命令行解决方法?

这看起来像是VS / Add Service Reference错误。另一种方法是手动纠正Reference.cs中的许多操作。

理想情况下,我正在寻找一种方法来轻松,自动地运行svcutil或Add Service Reference,以便Reference类正确并生成xamlx活动。一个很好的解释是为什么存在差异,以及在幕后发生的事情。

更新: 在控制台应用程序中生成的消息合同导致相同的问题 - 不正确的响应声明。如果使用参数而不是WF服务应用程序无法提供的消息,问题就会消失。

2 个答案:

答案 0 :(得分:2)

我对这些问题远非权威,虽然下面的回复可能不适合您的问题,但我最近与服务建立无代理连接的经验可能会为您或下一个人提供一些见解。类似的问题。

我首先看看你是否可以使用fiddler手动滚动SOAP请求,看看你是否能够创建正确的消息并发送它。由于您将自动化工具描述为错误(或者可能存在配置问题,因此您不会这样做)。无论哪种方式,明确了解合同的形状,并能够在小提琴手中进行可靠的测试可能会提供清晰度。

您不一定需要依赖使用代理。您可以创建自己的soap消息并以两种方式之一发送它。第一个是使用ChannelFactory。

  1. 创建您的邮件正文(如果需要,邮件类可以无需工作)
  2. 制作您的讯息
  3. 通过ChannelFactory发送消息
  4. 对于第1步,您可以通过制作一个简单的POCO来构建您的消息,以反映合同中的预期结果。您应该能够通过WSDL派生该类。

    让我们说服务是这样的:

    [ServiceContract(Namespace = "http://Foo.bar.car")]
    public interface IPolicyService
    {
        [OperationContract]
        PolicyResponse GetPolicyData(PolicyRequest request);
    }
    
    public class PolicyData : IPolicyService
    {
       public PolicyResponse GetPolicyData(PolicyRequest request)
       {
                var polNbr = request.REQ_POL_NBR;
                return GetMyData(polNbr);
        }
    }
    

    你需要一个像这样的课:

    [DataContract(Namespace = "http://Foo.bar.car")]
    public class GetPolicyData
    {
        [DataMember]
        public request request { get; set; }
    }
    
    [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy")]
    public class request
    {
        ///<summary>
        /// Define request parameter for SOAP API to retrieve selective Policy level data
        /// </summary>
        [DataMember]
        public string REQ_POL_NBR { get; set; }
    }
    

    然后你会这样称呼它:

        private static Message SendMessage(string id)
        {
            var body = new GetPolicyData {request =  new request{ REQ_POL_NBR = id }}; 
            var message = Message.CreateMessage(MessageVersion.Soap11, "http://Foo.bar.car/IPolicyService/GetPolicyData", body);
    // these headers would probably not be required, but added for completeness
            message.Headers.Add(MessageHeader.CreateHeader("Accept-Header", string.Empty, "application/xml+"));
            message.Headers.Add(MessageHeader.CreateHeader("Content-Type", string.Empty, "text/xml"));
            message.Headers.Add(MessageHeader.CreateHeader("FromSender", string.Empty, "DispatchMessage"));
            message.Headers.To = new System.Uri(@"http://localhost:5050/PolicyService.svc");
    
            var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
            {
                 MessageEncoding = WSMessageEncoding.Text,
                MaxReceivedMessageSize = int.MaxValue,
                SendTimeout = new TimeSpan(1, 0, 0),
                ReaderQuotas = { MaxStringContentLength = int.MaxValue, MaxArrayLength = int.MaxValue, MaxDepth = int.MaxValue }
            };
            message.Properties.Add("Content-Type", "text/xml; charset=utf-8");
            message.Properties.Remove("Accept-Encoding");
            message.Properties.Add("Accept-Header", "application/xml+");
    
            var cf = new ChannelFactory<IRequestChannel>(binding, new EndpointAddress(new Uri("http://localhost:5050/PolicyService.svc")));
    
            cf.Open();
            var channel = cf.CreateChannel();
            channel.Open();
    
            var result = channel.Request(message);
    
            channel.Close();
            cf.Close();
            return result;
        }
    

    您收到的内容将是一条消息,您将需要反序列化,并且有一些OOTB方法可以执行此操作,(Message.GetReaderAtBodyContents,Message.GetBody)保留w /手动主题:< / p>

        /// <summary>
    /// Class MessageTransform.
    /// </summary>
    public static class MessageTransform
    {
        /// <summary>
        /// Gets the envelope.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>XDocument.</returns>
        public static XDocument GetEnvelope(Message message)
        {
            using (var memoryStream = new MemoryStream())
            {
                var messageBuffer = message.CreateBufferedCopy(int.MaxValue);
                var xPathNavigator = messageBuffer.CreateNavigator();
    
                var xmlWriter = XmlWriter.Create(memoryStream);
                xPathNavigator.WriteSubtree(xmlWriter);
                xmlWriter.Flush();
                xmlWriter.Close();
    
                memoryStream.Position = 0;
                var xdoc = XDocument.Load(XmlReader.Create(memoryStream));
                return xdoc;
            }           
        }
    
        /// <summary>
        /// Gets the header.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>XNode.</returns>
        public static XNode GetHeader(Message message)
        {
            var xdoc = GetEnvelope(message);
    
            var strElms = xdoc.DescendantNodes();
            var header = strElms.ElementAt(1);
    
            return header;
        }
    
        /// <summary>
        /// Gets the body.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="localName">Name of the local.</param>
        /// <param name="namespaceName">Name of the namespace.</param>
        /// <returns>IEnumerable&lt;XElement&gt;.</returns>
        public static IEnumerable<XElement> GetBody(Message message, string localName, string namespaceName)
        {
            var xdoc = GetEnvelope(message);
    
            var elements = xdoc.Descendants(XName.Get(localName, namespaceName));
    
            return elements;
        }
    }
    

    或者您可以手动构建肥皂封套并使用WebClient:

    using System.Net; 
    using System.Xml.Linq;
    
    public static class ClientHelper
    {
        public static string Post(string targetUrl, string action, string method, string key, string value)
        {
            var request = BuildEnvelope(method, key, value);
        using (var webClient = new WebClient())
        {
            webClient.Headers.Add("Accept-Header", "application/xml+");
            webClient.Headers.Add("Content-Type", "text/xml; charset=utf-8");
            webClient.Headers.Add("SOAPAction", action);
            var result = webClient.UploadString(targetUrl, "POST", request);
    
            return result;
        }
    }
    
    public static string BuildEnvelope(string method, string key, string value)
    {
        XNamespace s = "http://schemas.xmlsoap.org/soap/envelope/";
        XNamespace d = "d4p1";
        XNamespace tempUri = "http://tempuri.org/";
        XNamespace ns = "http://Foo.bar.car";
        XNamespace requestUri = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy";
        var xDoc = new XDocument(
                            new XElement(
                                s + "Envelope",
                                new XAttribute(XNamespace.Xmlns + "s", s),
                                new XElement(
                                    s + "Body",
                                    new XElement(
                                        ns + method,
                                        new XElement(requestUri + "request", 
                                            new XElement(tempUri + key, value))
                                    )
                                )
                            )
                        );
        // hack - finish XDoc construction later
        return xDoc.ToString().Replace("request xmlns=", "request xmlns:d4p1=").Replace(key, "d4p1:" + key);
    }
    

    用以下方式调用:

    return ClientHelper.Post("http://localhost:5050/PolicyService.svc", "http://Foo.bar.car/IPolicyService/GetPolicyData", "GetPolicyData", "REQ_POL_NBR", id);
    

    在Fiddler中测试它看起来像这样:

    Post action:    http://localhost:5050/PolicyService.svc
    Header:
    User-Agent: Fiddler
    SOAPAction: http://Foo.bar.car/IPolicyService/GetPolicyData
    Content-type: text/xml
    Host: localhost:5050
    Content-Length: 381
    

    体:

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
      <s:Body>
    <GetPolicyData xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://Foo.bar.car">
    <request xmlns:d4p1="http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy">
    <d4p1:REQ_POL_NBR>1</d4p1:REQ_POL_NBR>
    </request>
    </GetPolicyData>
      </s:Body>
    </s:Envelope>
    

    同样,这个答案并不是试图解决如何以不同的方式调用svcUtil,而是为了避免完全调用它,所以我希望编辑之神不要为此而诅咒我; - )

    上面的代码受到了比我更好的开发人员的启发,但我希望它有所帮助。

    http://blogs.msdn.com/b/stcheng/archive/2009/02/21/wcf-how-to-inspect-and-modify-wcf-message-via-custom-messageinspector.aspx

答案 1 :(得分:0)

我建议您使用命令行实用程序生成wsdl代理,并在项目中添加生成的代理文件。它将适用于每个项目,您可以从output.config中找到所需的配置,这些配置将从命令行实用程序生成。

如果您需要wsdl命令和选项,那么我可以为您提供。