After providing the streaming method, wcf has always had a small problem and cannot find a suitable stream carrier. If you can use the file stream or something to return values, it would be better, but in more cases, you need to return a stream, but the Stream does not have a real carrier like a file, and sometimes the stream is large (if it is small, you do not need to use the streaming method ), at this time, it seems a little troublesome.
First of all, I don't like using MemoryStream because it actually occupies so much memory. In the case of a large amount of data, the power of the wcf streaming method will be greatly reduced. Of course, you can also use the file to return the file stream to bypass this problem, or use other ready-made streams.
However, I prefer to use logic similar to Circular Buffer to process this stream, which can occupy a small amount of memory to output a large volume of streams, the advantage of the streaming mode of wcf is maximized.
However, I checked several implementations of Circlar Buffer on the Internet, but I don't feel very satisfied... However, you can create one by yourself:
CircularStream public class CircularStream
: Stream
{
# Region Fields
Private const int DefaultCapacity = 0x10000;
Private readonly object SyncRoot = new object ();
Private bool m_closed;
Private readonly byte [] m_buffer;
Private int m_read;
Private int m_write;
# Endregion
# Region Ctors
Public CircularStream (int capacity)
{
M_buffer = new byte [capacity];
}
Public CircularStream (Action <Stream> action)
: This (defacapcapacity)
{
ThreadPool. QueueUserWorkItem (_ =>
{
Try
{
Action (this );
}
Finally
{
This. Dispose ();
}
});
}
# Endregion
# Region Overrides
Public override bool CanRead
{
Get {return true ;}
}
Public override bool CanSeek
{
Get {return false ;}
}
Public override bool CanWrite
{
Get {return true ;}
}
Public override void Flush (){}
Public override long Length
{
Get {throw new NotSupportedException ();}
}
Public override long Position
{
Get {throw new NotSupportedException ();}
Set {throw new NotSupportedException ();}
}
Public override long Seek (long offset, SeekOrigin origin)
{
Throw new NotSupportedException ();
}
Public override void SetLength (long value)
{
Throw new NotSupportedException ();
}
Protected override void Dispose (bool disposing)
{
Lock (SyncRoot)
{
M_closed = true;
Monitor. Pulse (SyncRoot );
}
Base. Dispose (disposing );
}
Public override int Read (byte [] buffer, int offset, int count)
{
Lock (SyncRoot)
{
While (m_read = m_write) // empty
If (m_closed)
Return 0;
Else
Monitor. Wait (SyncRoot, 100, true );
Int c;
If (m_write> m_read)
C = m_write-m_read;
Else
C = m_buffer.Length-m_read;
Monitor. Pulse (SyncRoot );
If (c> count)
{
Buffer. BlockCopy (m_buffer, m_read, buffer, offset, count );
M_read + = count;
Return count;
}
Else
{
Buffer. BlockCopy (m_buffer, m_read, buffer, offset, c );
M_read = (m_read + c) % m_buffer.Length;
Return c;
}
}
}
Public override void Write (byte [] buffer, int offset, int count)
{
Lock (SyncRoot)
{
If (m_closed)
Throw new ObjectDisposedException ("CircleBufferStream ");
While (count> 0)
{
While (m_write + 1) % m_buffer.Length = m_read) // full
Monitor. Wait (SyncRoot, 100, true );
If (m_read> m_write)
{
Int c = Math. Min (m_read-m_write-1, count );
Buffer. BlockCopy (buffer, offset, m_buffer, m_write, c );
M_write + = c;
Count-= c;
Offset + = c;
}
Else
{
Int c = m_buffer.Length-m_write;
If (m_read = 0)
-- C;
If (c> count)
{
Buffer. BlockCopy (buffer, offset, m_buffer, m_write, count );
M_write + = count;
Count = 0;
}
Else
{
Buffer. BlockCopy (buffer, offset, m_buffer, m_write, c );
M_write = (m_write + c) % m_buffer.Length;
Count-= c;
Offset + = c;
}
}
Monitor. Pulse (SyncRoot );
}
}
}
# Endregion
# Region Properties
Public int Capacity {get {return m_buffer.Length ;}}
# Endregion
}
Add related contracts and implementations:
Contract [ServiceContract]
Public interface IService1
{
[OperationContract]
Stream GetStream (int value );
} Implement public class Service1: IService1
{
Public Stream GetStream (int value)
{
Return new CircularStream (s =>
New XStreamingElement ("root ",
From I in Enumerable. Range (1, value)
Select new XElement ("item ",
New XAttribute ("id", I). Save (s ));
}
}
And the called services and clients:
Service and client static class Program
{
Static void Main (string [] args)
{
Var host = new ServiceHost (typeof (Service1 ));
Host. Open ();
Var binding = new BasicHttpBinding ();
Binding. TransferMode = TransferMode. StreamedResponse;
Binding. maxcompute edmessagesize = int. MaxValue;
// If you don't think you are addicted, you can turn on the loop.
// For (int I = 0; I <100; I ++)
//{
New Uri ("http: // localhost: 12123/"). InvokeWcfClient <IService1> (binding, c =>
{
Var stream = c. GetStream (10000 );
String line;
Using (var r = new StreamReader (stream ))
While (null! = (Line = r. ReadLine ()))
Console. WriteLine (line );
});
//}
Host. Close ();
}
Public static void InvokeWcfClient <TChannel> (
This Uri uri, Binding binding, Action <TChannel> action)
Where TChannel: class
{
TChannel client = ChannelFactory <TChannel>. CreateChannel (
Binding, new EndpointAddress (uri ));
Var co = (ICommunicationObject) client;
Try
{
Co. Open ();
Action (client );
}
Finally
{
If (co. State = CommunicationState. Faulted)
Co. Abort ();
Else
Co. Close ();
}
}
}
In this way, you can run it perfectly.