.net 如何从WCF服务返回流?

.net 如何从WCF服务返回流?,.net,wcf,stream,protobuf-net,.net,Wcf,Stream,Protobuf Net,我在玩protobuf网络和WCF。以下是我创建的代码: public class MobileServiceV2 { [WebGet(UriTemplate = "/some-data")] [Description("returns test data")] public Stream GetSomeData() { WebOperationContext.Current.OutgoingResponse.ContentType = "appl

我在玩protobuf网络和WCF。以下是我创建的代码:

public class MobileServiceV2
{
    [WebGet(UriTemplate = "/some-data")]
    [Description("returns test data")]
    public Stream GetSomeData()
    {
        WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-protobuf";

        var ms = new MemoryStream();
        ProtoBuf.Serializer.Serialize(ms, new MyResponse { SomeData = "Test data here" });
        return ms;
    }
}

[DataContract]
public class MyResponse
{
    [DataMember(Order = 1)] 
    public string SomeData { get; set; }
}
当我查看Fiddler时,我可以看到正确的传出内容类型,所有内容看起来都很好,但我得到的是空的响应。IE提示下载文件,但该文件为空。序列化程序不工作吗?还是我做得不对

编辑:

我在方法中添加了以下代码,是的,它可以正确地序列化。我如何从WCF返回流时出错

using (var file = File.Create("C:\\test.bin"))
        {
            Serializer.Serialize(file, new MyResponse { SomeData = "Test data here" });
        }
请往这边走

var ms = new MemoryStream();
using (var file = File.Create("C:\\test.bin"))
{
Serializer.Serialize(file, new MyResponse { SomeData = "Test data here" });
file.CopyTo(ms); 
}

return ms;//stream

只需写一个MemoryStream,然后倒带它。在这种情况下,不要处理它:

var ms = new MemoryStream();
Serializer.Serialize(ms, obj);
ms.Position = 0;
return ms;

然而,这并不意味着它会在内存中缓冲。我可以试着想出一些巫毒来避免这种情况,但这将是非常复杂的。

如果我正确理解这个问题,您正在尝试使用流式WCF绑定。在这种情况下,您可以尝试将数据拆分为块,这些块在客户端上以相同的方式分别序列化和反序列化。唯一需要注意的是接收端WCF提供的流实现——您需要自己包装它并管理读取。以下是我用来促进这一点的一个类:

    public static class StreamingUtility
{

    public static IEnumerable<T> FromStream<T>(this Stream value, Action<T> perItemCallback = null)
    {

        List<T> result = new List<T>();
        StreamProxy sp = new StreamProxy(value);
        try
        {
            while (sp.CanRead)
            {

                T v = ProtoBuf.Serializer.DeserializeWithLengthPrefix<T>((Stream)sp, ProtoBuf.PrefixStyle.Base128);
                if (perItemCallback != null)
                    perItemCallback(v);

                result.Add(v);
            }
        }
        catch { }

        return result;
    }

    public static StreamingContent<T> SingleToStream<T>(this T value)
    {
        return new StreamingContent<T>(new T[] { value });
    }

    public static StreamingContent<T> ToStream<T>(this IEnumerable<T> value)
    {
        return new StreamingContent<T>(value);
    }

    public class StreamingContent<T> : Stream
    {
        private bool _canRead = true;
        private ManualResetEventSlim _dataIsReady = new ManualResetEventSlim(false);
        private bool _noMoreAdditions = false;
        private long _readingOffset = 0;

        //private IFormatter _serializer = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.CrossMachine));
        private IEnumerable<T> _source = null;

        private MemoryStream _stream = new MemoryStream();

        public static StreamingContent<T> Clone(Stream origin)
        {
            return new StreamingContent<T>(origin);
        }

        private StreamingContent(Stream origin)
        {
            byte[] b = new byte[65536];

            while (true)
            {
                int count = origin.Read(b, 0, b.Length);

                if (count > 0)
                {
                    _stream.Write(b, 0, count);
                }
                else
                    break;
            }
            _noMoreAdditions = true;
        }

        public StreamingContent(IEnumerable<T> source)
        {
            if (!s_initialized)
            {
                StreamingUtility.Initialize();

                StreamingUtility.s_initialized = true;
            }

            _source = source.ToList();
            if (source.Count() > 0)
            {
                new Thread(new ParameterizedThreadStart(obj =>
                {
                    StreamingContent<T> _this = obj as StreamingContent<T>;
                    foreach (T item in _this._source)
                    {
                        lock (_this._stream)
                        {
                            if (_this._noMoreAdditions) break;
                            _stream.Seek(0, SeekOrigin.End);

                            ProtoBuf.Serializer.SerializeWithLengthPrefix<T>(_this._stream, item, ProtoBuf.PrefixStyle.Base128);

                            //_serializer.Serialize(_this._stream, item);
                            _dataIsReady.Set();
                        }
                    }

                    lock (_this._stream)
                    {
                        _this._noMoreAdditions = true;
                        _dataIsReady.Set();
                    }
                })) { IsBackground = true }.Start(this);
            }
            else
            {
                _canRead = false;
            }
        }

        public override bool CanRead
        {
            get { return _canRead; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get
            {
                while (!_noMoreAdditions) Thread.Sleep(20);
                return _stream.Length;
            }
        }

        public override long Position
        {
            get
            {
                throw new Exception("This stream does not support getting the Position property.");
            }
            set
            {
                throw new Exception("This stream does not support setting the Position property.");
            }
        }

        public override void Close()
        {
            lock (_stream)
            {
                _noMoreAdditions = true;
                _stream.Close();
            }
        }

        public override void Flush()
        {
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (!CanRead) return 0;

            bool wait = false;

            lock (_stream)
            {
                wait = !_dataIsReady.IsSet && !_noMoreAdditions;
            }

            if (wait)
            {
                _dataIsReady.Wait();
            }

            lock (_stream)
            {
                if (!_noMoreAdditions)
                    _dataIsReady.Reset();

                if (_stream.Length > _readingOffset)
                {
                    _stream.Seek(_readingOffset, SeekOrigin.Begin);
                    int res = _stream.Read(buffer, 0, count);

                    if (_noMoreAdditions && count + _readingOffset >= _stream.Length)
                        _canRead = false;

                    _readingOffset += res;

                    return res;
                }
            }

            return 0;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new Exception("This stream does not support seeking.");
        }

        public override void SetLength(long value)
        {
            throw new Exception("This stream does not support setting the Length.");
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new Exception("This stream does not support writing.");
        }

        protected override void Dispose(bool disposing)
        {
            try
            {
                lock (_stream)
                {
                    _noMoreAdditions = true;
                    _stream.Close();
                }
            }
            catch { }
        }
    }

    private class StreamProxy : Stream
    {
        private bool _canRead = true;
        private bool _endOfMessage = false;
        private Stream _internalStream;
        private int _readPosition = 0;
        private MemoryStream _storage = new MemoryStream();
        private int _writePosition = 0;

        public StreamProxy(Stream internalStream)
        {
            _internalStream = internalStream;
            byte[] initialRequest = new byte[1000];

            int length = _internalStream.Read(initialRequest, 0, 1000);

            if (length != 0)
                _storage.Write(initialRequest, 0, length);
            else
                _canRead = false;

            _writePosition = length;
        }

        public override bool CanRead
        {
            get { return _canRead; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get { throw new NotImplementedException(); }
        }

        public override long Position
        {
            get
            {
                return _readPosition;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void Flush()
        {
        }

        public override int ReadByte()
        {
            byte[] res = new byte[1];
            int g = Read(res, 0, 1);
            return res[0];
        }

        public override int Read(byte[] buffer, int offset, int count)
        {


            int res = 0;
            if (_readPosition + count > _writePosition)
            {
                /// add extra bytes to see if more data is available and we need to allow next read
                int readSize = _readPosition + count - _writePosition;

                if (readSize < 1024)
                    readSize = 1024;

                byte[] read = new byte[readSize];
                res = _internalStream.Read(read, 0, readSize);
                if (res > 0)
                {
                    _storage.Seek(_writePosition, SeekOrigin.Begin);
                    _writePosition += res;
                    _storage.Write(read, 0, res);

                }
                else if (res == 0)/// If the read returned 0, we're at the end
                {
                    _endOfMessage = true;

                }

                if (res > 0 && res < readSize)
                {
                    read = new byte[1024];
                    res = _internalStream.Read(read, 0, 1024);
                    if (res > 0)
                    {
                        _storage.Seek(_writePosition, SeekOrigin.Begin);
                        _writePosition += res;
                        _storage.Write(read, 0, res);

                    }
                    else if (res == 0)/// If the read returned 0, we're at the end
                    {
                        _endOfMessage = true;

                    }
                }
            }

            _storage.Seek(_readPosition, SeekOrigin.Begin);
            res = _storage.Read(buffer, offset, count);
            _readPosition += res;


            /// If end of message was reached and all the data was read from the
            /// internal storage, mark CanRead as false
            if (_readPosition >= _writePosition && _endOfMessage)
                _canRead = false;

            return res;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }
    }
}
公共静态类StreamingUtility
{
公共静态IEnumerable FromStream(此流值,Action perItemCallback=null)
{
列表结果=新列表();
StreamProxy sp=新的StreamProxy(值);
尝试
{
while(sp.CanRead)
{
tV=ProtoBuf.Serializer.DeserializeWithLengthPrefix((Stream)sp,ProtoBuf.PrefixStyle.Base128);
if(perItemCallback!=null)
围手术期(v);
结果:添加(v);
}
}
捕获{}
返回结果;
}
公共静态流化内容SingleToStream(此T值)
{
返回新的StreamingContent(新的T[]{value});
}
公共静态流化内容到流(此IEnumerable值)
{
返回新的StreamingContent(值);
}
公共类StreamingContent:Stream
{
私有bool_canRead=true;
private ManualResetEventSlim\u dataIsReady=新的ManualResetEventSlim(假);
私有bool _noMoreAdditions=false;
专用长_readingOffset=0;
//私有IFormatter_serializer=新的二进制格式化程序(null,新的StreamingContext(streamingContextState.CrossMachine));
私有IEnumerable _source=null;
私有内存流_stream=新内存流();
公共静态流化内容克隆(流源)
{
返回新的StreamingContent(源文件);
}
私有StreamingContent(流源)
{
字节[]b=新字节[65536];
while(true)
{
int count=origin.Read(b,0,b.Length);
如果(计数>0)
{
_stream.Write(b,0,count);
}
其他的
打破
}
_NOMOREAD加法=真;
}
公共流化内容(IEnumerable源)
{
如果(!s_已初始化)
{
StreamingUtility.Initialize();
StreamingUtility.s_initialized=true;
}
_source=source.ToList();
if(source.Count()>0)
{
新线程(新参数化线程启动(obj=>
{
StreamingContent _this=obj作为StreamingContent;
foreach(T项在_this._source中)
{
锁定(_this._stream)
{
如果(_this._noMoreAdditions)中断;
_stream.Seek(0,SeekOrigin.End);
ProtoBuf.Serializer.SerializeWithLengthPrefix(_this._stream,item,ProtoBuf.PrefixStyle.Base128);
//_serializer.Serialize(_this._stream,item);
_dataIsReady.Set();
}
}
锁定(_this._stream)
{
_这是.\u noMoreAdditions=true;
_dataIsReady.Set();
}
})){IsBackground=true}.Start(this);
}
其他的
{
_canRead=false;
}
}
公共覆盖布尔可读取
{
获取{return\u canRead;}
}
公共覆盖布尔搜索
{
获取{return false;}
}
公共覆盖布尔可写
{
获取{return false;}
}
公共覆盖长长度
{
得到
{
while(!_nomore)线程。Sleep(20);
返回流长度;
}
}
公众优先多头仓位
{
得到
{
抛出新异常(“此流不支持获取Position属性”);
}
设置
{
抛出新异常(“此流不支持设置Position属性”);
}
}
公共覆盖无效关闭()
{
锁(_流)
{
_NOMOREAD加法=真;
_stream.Close();
}
}
公共覆盖无效刷新()
{
}
公共重写整型读取(字节[]缓冲区、整型偏移量、整型计数)
{
如果(!CanRead)返回0;
bool wait=false;
锁(_流)
{
wait=!\u dataIsReady.IsSet&&!\u noMoreAdditions;
}
如果(等待)
{
_dataIsReady.Wait();
}
锁(_流)
{
如果(!\u nomore添加)
_dataIsReady.Reset();
如果
IEnumerable<SomeType> collection = ...
clannel.Method(collection.ToStream());
public void Method(Stream stream){
   IEnumerable<SomeType> coll = stream.FromStream<SomeType>();
}