none
Exception: For request in operation UploadFile to be a stream the operation must have a single parameter whose type is Stream

    Question

  • Hello

    I am using NetTcpBinding to upload files to my WCF Service. The operation signature is something like this

     

    [OperationContract]   
    string UploadFile(Stream content, string fileName);
    

     

    I found that changing the TransferMode to Streamed might help to reduce the memory consumption of my application, so I tried

    myBinding.TransferMode = TransferMode.Streamed; 
    

    However, when I am running my application, I get a InvalidOperationException exception that says:

    For request in operation UploadFile to be a stream the operation must have a single parameter whose type is Stream.

    I searched the web for similar problems, and found some REST examples, but I would like to continue with a NetTcpBinding.

    Is there a way to use streaming for one parameter and buffering for the second parameter?

    I could think of some workarounds, but they are dirty and ugly, for example:

    • I could create two functions insted of just one, one for uploading the file content (returning a "file token") and one for the metadata (which needs to pass in the "file token" as well).
    • Or I could somehow inject the metadata at the beginning of the stream, but how ugly is that?

    I hope someone has a nicer idea.

     

    Kind Regards

    Mat

     

     

     

    Tuesday, January 11, 2011 4:49 PM

Answers

  • You can still use NetTcpBinding, because the restriction is that the request (or response) can have only 1 body parameter of type stream (maybe the exception message should be improved). Just like in the REST case, where you can move the fileName parameter outside the body (in most cases, to the URI), in the SOAP case you can move the fileName parameter to one of the SOAP headers. To do that, you need to start using a MessageContract, which is a class which defines exactly which parameters should go in the message body, and which ones should go in the message headers.

    The code below shows your operation with message contract. In the example I'm passing a >1GB "file" to the server, and the memory consumption of my application stays really low (which wouldn't happen if the message had been buffered).

      public class Post_6a7ae6fd_e15f_4e75_ae3f_47b601f0707e
      {
        [MessageContract]
        public class UploadFileRequest
        {
          [MessageHeader]
          public string fileName;
          [MessageBodyMember]
          public Stream content;
        }
        [MessageContract]
        public class UploadFileResponse
        {
          [MessageBodyMember]
          public string UploadFileResult;
        }
        [ServiceContract]
        public interface ITest
        {
          [OperationContract]
          UploadFileResponse UploadFile(UploadFileRequest input);
        }
        public class Service : ITest
        {
          static long CountBytes(Stream stream)
          {
            byte[] buffer = new byte[100000];
            int bytesRead;
            long totalBytesRead = 0;
            do
            {
              bytesRead = stream.Read(buffer, 0, buffer.Length);
              totalBytesRead += bytesRead;
            } while (bytesRead > 0);
            return totalBytesRead;
          }
          public UploadFileResponse UploadFile(UploadFileRequest input)
          {
            long byteCount = CountBytes(input.content);
            string result = string.Format("File {0} has {1} bytes", input.fileName, byteCount);
            return new UploadFileResponse { UploadFileResult = result };
          }
        }
    
        class MyReadonlyStream : Stream
        {
          long length;
          long leftToRead;
          public MyReadonlyStream(long length)
          {
            this.length = length;
            this.leftToRead = length;
          }
    
          public override bool CanRead
          {
            get { return true; }
          }
    
          public override bool CanSeek
          {
            get { return false; }
          }
    
          public override bool CanWrite
          {
            get { return false; }
          }
    
          public override void Flush()
          {
          }
    
          public override long Length
          {
            get { return this.length; }
          }
    
          public override long Position
          {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
          }
    
          public override int Read(byte[] buffer, int offset, int count)
          {
            int toReturn = (int)Math.Min(this.leftToRead, (long)count);
            this.leftToRead -= toReturn;
            return toReturn;
          }
    
          public override long Seek(long offset, SeekOrigin origin)
          {
            throw new NotSupportedException();
          }
    
          public override void SetLength(long value)
          {
            throw new NotSupportedException();
          }
    
          public override void Write(byte[] buffer, int offset, int count)
          {
            throw new NotSupportedException();
          }
        }
    
        static Binding GetBinding()
        {
          NetTcpBinding result = new NetTcpBinding();
          result.TransferMode = TransferMode.Streamed;
          result.MaxReceivedMessageSize = int.MaxValue;
          return result;
        }
    
        public static void Test()
        {
          string baseAddress = "net.tcp://localhost:8000/Service";
          ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
          host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
          host.Open();
    
          ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
          ITest proxy = factory.CreateChannel();
    
          Stream fileToBeUploaded = new MyReadonlyStream(1234567890);
          var result = proxy.UploadFile(new UploadFileRequest { fileName = "TheFile.bin", content = fileToBeUploaded });
          Console.WriteLine(result.UploadFileResult);
    
          Console.WriteLine("Press ENTER to close");
          Console.ReadLine();
          ((IClientChannel)proxy).Close();
          factory.Close();
          host.Close();
        }
      }
    
    • Marked as answer by Mat Hes Thursday, January 13, 2011 10:58 AM
    Thursday, January 13, 2011 3:29 AM

All replies

  • Mat,

    Streaming operations can only have one parameter of type Stream. You can find more information about WCF and streaming here: http://msdn.microsoft.com/en-us/library/ms733742.aspx.  Another possibility is to use chunking, that is breaking up the data and sending it a chunk at a time. You can find more information about that here: http://msdn.microsoft.com/en-us/library/aa717050.aspx

    I hope this is helpful,

    Michael Green
    WCF Documentation Team

    Tuesday, January 11, 2011 5:59 PM
  • Alternatively, you can use REST services. In WCF 4, when using REST services, a streaming operation does not have to provide a single parameter of type Stream.
    Lante, shanaolanxing This posting is provided "AS IS" with no warranties, and confers no rights.
    Windows Azure Technical Forum Support Team Blog
    Thursday, January 13, 2011 2:31 AM
  • You can still use NetTcpBinding, because the restriction is that the request (or response) can have only 1 body parameter of type stream (maybe the exception message should be improved). Just like in the REST case, where you can move the fileName parameter outside the body (in most cases, to the URI), in the SOAP case you can move the fileName parameter to one of the SOAP headers. To do that, you need to start using a MessageContract, which is a class which defines exactly which parameters should go in the message body, and which ones should go in the message headers.

    The code below shows your operation with message contract. In the example I'm passing a >1GB "file" to the server, and the memory consumption of my application stays really low (which wouldn't happen if the message had been buffered).

      public class Post_6a7ae6fd_e15f_4e75_ae3f_47b601f0707e
      {
        [MessageContract]
        public class UploadFileRequest
        {
          [MessageHeader]
          public string fileName;
          [MessageBodyMember]
          public Stream content;
        }
        [MessageContract]
        public class UploadFileResponse
        {
          [MessageBodyMember]
          public string UploadFileResult;
        }
        [ServiceContract]
        public interface ITest
        {
          [OperationContract]
          UploadFileResponse UploadFile(UploadFileRequest input);
        }
        public class Service : ITest
        {
          static long CountBytes(Stream stream)
          {
            byte[] buffer = new byte[100000];
            int bytesRead;
            long totalBytesRead = 0;
            do
            {
              bytesRead = stream.Read(buffer, 0, buffer.Length);
              totalBytesRead += bytesRead;
            } while (bytesRead > 0);
            return totalBytesRead;
          }
          public UploadFileResponse UploadFile(UploadFileRequest input)
          {
            long byteCount = CountBytes(input.content);
            string result = string.Format("File {0} has {1} bytes", input.fileName, byteCount);
            return new UploadFileResponse { UploadFileResult = result };
          }
        }
    
        class MyReadonlyStream : Stream
        {
          long length;
          long leftToRead;
          public MyReadonlyStream(long length)
          {
            this.length = length;
            this.leftToRead = length;
          }
    
          public override bool CanRead
          {
            get { return true; }
          }
    
          public override bool CanSeek
          {
            get { return false; }
          }
    
          public override bool CanWrite
          {
            get { return false; }
          }
    
          public override void Flush()
          {
          }
    
          public override long Length
          {
            get { return this.length; }
          }
    
          public override long Position
          {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
          }
    
          public override int Read(byte[] buffer, int offset, int count)
          {
            int toReturn = (int)Math.Min(this.leftToRead, (long)count);
            this.leftToRead -= toReturn;
            return toReturn;
          }
    
          public override long Seek(long offset, SeekOrigin origin)
          {
            throw new NotSupportedException();
          }
    
          public override void SetLength(long value)
          {
            throw new NotSupportedException();
          }
    
          public override void Write(byte[] buffer, int offset, int count)
          {
            throw new NotSupportedException();
          }
        }
    
        static Binding GetBinding()
        {
          NetTcpBinding result = new NetTcpBinding();
          result.TransferMode = TransferMode.Streamed;
          result.MaxReceivedMessageSize = int.MaxValue;
          return result;
        }
    
        public static void Test()
        {
          string baseAddress = "net.tcp://localhost:8000/Service";
          ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
          host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
          host.Open();
    
          ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
          ITest proxy = factory.CreateChannel();
    
          Stream fileToBeUploaded = new MyReadonlyStream(1234567890);
          var result = proxy.UploadFile(new UploadFileRequest { fileName = "TheFile.bin", content = fileToBeUploaded });
          Console.WriteLine(result.UploadFileResult);
    
          Console.WriteLine("Press ENTER to close");
          Console.ReadLine();
          ((IClientChannel)proxy).Close();
          factory.Close();
          host.Close();
        }
      }
    
    • Marked as answer by Mat Hes Thursday, January 13, 2011 10:58 AM
    Thursday, January 13, 2011 3:29 AM
  • Carlos


    This is exactly what I was looking for, it works like a charm!

     

    Thank you very much!

     

    -Mat

     

    Thursday, January 13, 2011 11:02 AM
  • Alternatively, you can use REST services. In WCF 4, when using REST services, a streaming operation does not have to provide a single parameter of type Stream.
    Lante, shanaolanxing This posting is provided "AS IS" with no warranties, and confers no rights.
    Windows Azure Technical Forum Support Team Blog
    Hi, I am getting stuck on this one - Using one of the samples from MSDN -
    This is the service contract

    [OperationContract, WebInvoke(Method = "POST", UriTemplate = "File/{fileName}")]
    bool UploadFile(string fileName, Stream fileContents);

    I am getting this error and really want to use the REST here

    if you can give me some sample code with he service hosted in IIS - I would really appreciate it


    Amol Wankhede
    Wednesday, December 07, 2011 12:21 AM
  • REST-style service uses a WebServiceHost as its service base class instead of just a ServiceHost.

    I can vouch for this fixing the error, as I was having the same problem.

    Friday, September 21, 2012 3:18 PM