如何使用未包装的请求和包装的响应创建流操作? [英] How to create a streaming operation with a non-wrapped request and a wrapped response?

查看:86
本文介绍了如何使用未包装的请求和包装的响应创建流操作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好

我想在C#ServiceContract中创建流式下载操作.我希望响应是流式的,流中包含一个额外的元素(IsWrapped = true).此操作的请求将使用xsd.exe生成,因此不应进行包装 (IsWrapped = false).

I want to create a streaming download operation in a C# ServiceContract. I want the response to be streaming, with the Stream wrapped in an extra element (IsWrapped=true). The request for this operation will be generated using xsd.exe, so should not be wrapped (IsWrapped=false).

我创建了我的服务,并用SoapUI对其进行了测试,结果显示出预期的结果.在额外元素中带有Mtom附件的响应.但是,当我在Visual Studio中将服务引用添加到我的服务中以进行单元测试时,生成的代码包含 类中具有包装器名称的byte []而不是消息顶层的Stream,因此,整个响应将在客户端进行缓冲.具有最大1,5GB的响应,这是不可接受的.

I created my service and testing it with SoapUI showed the expected results. A response with an Mtom attachment in an extra element. When I add a service reference to my service in Visual studio for unittesting purposes however, the generated code contains a byte[] in a class with the name of the wrapper instead of a Stream in the top level of the message, so the entire response will be buffered at client side. Having responses of maximum 1,5GB, this is unacceptable.

当我也更改要包装的请求(IsWrapped = true)时,响应的确在客户端生成的代码中包含Stream.我尝试使用添加服务引用"是Visual Studio和svcutil.exe来生成客户端代码,两者的结果相同.

When I change the request to be wrapped (IsWrapped=true) as well, the response does contain a Stream in the client generated code. I've tried 'Add service reference' is Visual Studio and svcutil.exe to generate client code, both with the same results.

问题:未包装请求消息(IsWrapped = false)和响应消息被包装(IsWrapped = true)时,如何生成包含Stream而不是byte []的客户端代码? (如果这需要更改服务,那很好,因为 还没生产)

Question: How can I generate client code containing a Stream instead of a byte[] when the request message is not wrapped (IsWrapped=false) and the response message is wrapped (IsWrapped=true)? (If this requires changes in the service, that's fine, because it is not in Production yet)

请参见下面的简化代码示例,以说明我的问题:

See a simplified code sample below, illustrating my problem:

简化的服务合同:

[ServiceContract]
public interface IBasicService
{
    [OperationContract]
    ResponseMessage GetData(RequestMessage request);
}


简化的服务实现:

public class BasicService : IBasicService
{
    public ResponseMessage GetData(RequestMessage request)
    {
        var response = new ResponseMessage
        {
            Contents = new MemoryStream(Encoding.UTF8.GetBytes(request.RequestText))
        };

        return response;
    }
}


简化的请求消息定义:

// Change this to IsWrapped=true. That will have 'Add Service reference' Generate a Stream for the Response message....Why does it matter what the request looks like?!
[MessageContract(IsWrapped=false)]
public class RequestMessage
{
    [MessageBodyMember]
    public string RequestText { get; set; }
}


简化的响应消息定义:

[MessageContract(IsWrapped = true, WrapperName="WrapperElement", WrapperNamespace="http://WrapperNamespace")]
public class ResponseMessage
{
    [MessageBodyMember]
    public System.IO.Stream Contents { get; set; }
}


Web.config

Web.config

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="WrappedBinding" 
                 transferMode="StreamedResponse" 
                 messageEncoding="Mtom" />
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="Wrapped.BasicService">
        <endpoint address="" 
                  binding="basicHttpBinding" 
                  bindingConfiguration="WrappedBinding"
                  contract="Wrapped.IBasicService" />
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>


当请求消息具有IsWrapped = false时,添加服务引用"中的结果代码,请注意带有包装器名称和de byte []属性的类:

Resulting code from 'Add Service Reference' when request message has IsWrapped=false, note the class with the name of the wrapper and de byte[] property:

// Generated method
public Tests.BasicService.WrapperElement GetData(string RequestText) {
            Tests.BasicService.RequestMessage inValue = new Tests.BasicService.RequestMessage();
            inValue.RequestText = RequestText;
            Tests.BasicService.ResponseMessage retVal = ((Tests.BasicService.IBasicService)(this)).GetData(inValue);
            return retVal.WrapperElement;
        }

//Generated response class
[System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
    [System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
    public partial class ResponseMessage {

        [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://WrapperNamespace", Order=0)]
        public Tests.BasicService.WrapperElement WrapperElement;

        public ResponseMessage() {
        }

        public ResponseMessage(Tests.BasicService.WrapperElement WrapperElement) {
            this.WrapperElement = WrapperElement;
        }
    }

/// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34230")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://WrapperNamespace")]
    public partial class WrapperElement : object, System.ComponentModel.INotifyPropertyChanged {

        // NOTE THE byte[] HERE!
        private byte[] contentsField;

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Namespace="http://tempuri.org/", DataType="base64Binary", Order=0)]
        public byte[] Contents {
            get {
                return this.contentsField;
            }
            set {
                this.contentsField = value;
                this.RaisePropertyChanged("Contents");
            }
        }

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null)) {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }


当请求的IsWrapped = true时生成的代码,请注意,流现在是MessageBodyMember:

Generated code when the request has IsWrapped = true, note the Stream is now a MessageBodyMember:

// Generated method
public System.IO.Stream GetData(string RequestText) {
            Tests.BasicService.RequestMessage inValue = new Tests.BasicService.RequestMessage();
            inValue.RequestText = RequestText;
            Tests.BasicService.ResponseMessage retVal = ((Tests.BasicService.IBasicService)(this)).GetData(inValue);
            return retVal.Contents;
        }

// Generated reponse message
[System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
    [System.ServiceModel.MessageContractAttribute(WrapperName="WrapperElement", WrapperNamespace="http://WrapperNamespace", IsWrapped=true)]
    public partial class ResponseMessage {

        [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://tempuri.org/", Order=0)]
        // NOTE THAT THIS DOES CONTAIN A Stream.
        public System.IO.Stream Contents;

        public ResponseMessage() {
        }

        public ResponseMessage(System.IO.Stream Contents) {
            this.Contents = Contents;
        }
    }


推荐答案

我创建了服务,并使用SoapUI对其进行了测试,结果显示出预期的结果.在额外元素中带有Mtom附件的响应.但是,当我在Visual Studio中将服务引用添加到我的服务中以进行单元测试时,生成的代码 在类中包含一个具有包装器名称的byte []而不是消息顶层的Stream,因此整个响应将在客户端进行缓冲.具有最大1,5GB的响应,这是不可接受的.
I created my service and testing it with SoapUI showed the expected results. A response with an Mtom attachment in an extra element. When I add a service reference to my service in Visual studio for unittesting purposes however, the generated code contains a byte[] in a class with the name of the wrapper instead of a Stream in the top level of the message, so the entire response will be buffered at client side. Having responses of maximum 1,5GB, this is unacceptable.

您可以在服务端将其定义为List,Stream或codd-knows-什么.
实际上将传输的是XML文档中的base64编码的字节数组,一旦完全接收,将在另一端进行解析.

You can define it as List, Stream or codd-knows-what on the service side.
What will actually get transported is a base64 encoded byte array in a XML document, to be parsed on the other end once completely recieved.

毫无意义地传输它. SOAP只处理XML文档中的完整返回值,并且会自动将任何内容转换为更原始的类型(对数组的任何归类),这在编程语言中是毫无疑问的.
互操作性是SOAP中的重要因素,而简单性是归档它的最佳方法.

There is no point transmitting it as anything else. SOAP only deals with the full return value in a XML document and autoamtically turns anything into a more primitive type (any collction to array) that is unambigious among programming languages.
Interoprability was big factor in SOAP, and simplicity is the best way to archieve it.

如果要定义有关如何传输和解析这些消息的详细信息(包括避免在处理之前必须完全传输消息),则SOAP可能是错误的工具.然而 根据维基百科的 SOAP规范具有以下内容:
" SOAP基础协议绑定框架,描述了用于定义与基础协议的绑定的规则,该规则可用于在SOAP节点之间交换SOAP消息"

If you want to define details of how those messages get transmitted and parsed (inlcuding avoiding having to fully transmit the message before processing), SOAP is likely the wrong tool. However SOAP specifications according to wikipedia have this:
"The SOAP underlying protocol binding framework describing the rules for defining a binding to an underlying protocol that can be used for exchanging SOAP messages between SOAP nodes"

如果要以控制的突发传输(而不是一次全部传输),那可能是一种方法.当然,仅采用手动传输方式仍然效率较低.

If you want to transmit in contrlled bursts (rather then all at once), that migh be a way. Of course it is still a level less efficient to just move to a more manual way of transmission.


这篇关于如何使用未包装的请求和包装的响应创建流操作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆