C# POCO 到 Stream/Byte[] 并在通过 WCF 使用 Silverlight 4 客户端读取时进行处理

发布于 2024-10-21 05:23:21 字数 3174 浏览 1 评论 0原文

对我来说,Silverlight 中令人烦恼的部分之一是它不允许您直接返回 Stream,因为 Silverlight 会将其转换为 Byte[],即使使用TransferMode.StreamedResponse时。使用该技术,可以从 Silverlight 客户端以流式传输方式处理类似于 FileStream 的内容(参见下面的代码)。

然而,我想对非常大的 C# POCO 做类似的事情。如果我可以接收 Stream,我可以在两端使用 XML 序列化之类的东西,并在读取记录时对其进行处理。但是,由于返回值必须是 Byte[],因此我必须先读取到数组的末尾,然后才能处理它。这个方法至少可以让我向用户提供一些反馈。

我对可能的解决方案提出了两种可能性。一种是编写我自己的序列化器,或者至少是反序列化器,在其中处理正在读取的数据(我正在考虑分隔文本)。另一种想法是做某种类型的可变位Stream类,我相信这对我来说需要一个陡峭的学习曲线。我正在寻找有关如何在读取 Byte[] 中的值时处理这些值的任何想法或链接。

服务合同:

public static class Action {
    public static string val = "http://tempuri.org/stream";
}

// Share contract between client and server
[ServiceContract]
public interface IFileService {
#if SILVERLIGHT
    [OperationContract(AsyncPattern = true, Action = Action.val, ReplyAction = Action.val)]
    IAsyncResult BeginReadFile(Message request, AsyncCallback callback, object AsyncState);
    system.ServiceMode.Channels.Message EndReadFile(IAsyncResult result);
#else
    [OperationContract(AsyncPattern = true, Action = Action.val, ReplyAction = Action.val)]
    IAsyncResult BeginReadFile(string filePath, AsyncCallback callback, object AsyncState);
    system.IO.Stream EndReadFile(IAsyncResult result);
#endif
}

[DataContract(Name = "ReadFile", Namespace = "http://tempuri.org/")]
public class StreamRequest
{
    [DataMember(Order = 1)]
    public string FileName;
}

服务实施:

public IAsyncResult BeginReadFile(string filePath, AsyncCallback callback, object AsyncState) {
    return new CompletedAsyncResult<Stream>(File.OpenRead(filePath));
}

public Stream EndReadFile(IAsyncResult result) {
    return ((CompletedAsyncResult<Stream>)result).Data;
}

客户:

public void ReadFile( string filePath ) {
    BackToUIThread backToUIThread = byteCount => {
        this.OnBytesReceived(this, new AsyncEventArgs<object>(byteCount));
    };

    StreamRequest request = new StreamRequest() { FileName = "C:\\test.txt" };
    Message mIn = Message.CreateMessage(
        _myFactory.Endpoint.Binding.MessageVersion, Action.val, request);

    _myService.BeginReadFile(mIn,
        (asyncResult) =>
        {
            Message mOut = _myService.EndReadFile(asyncResult);
            System.Xml.XmlDictionaryReader r = mOut.GetReaderAtBodyContents();
            r.ReadToDescendant("ReadFileResult"); // move to stream
            r.Read(); // move to content

            int bytesRead = 1;
            long totalBytesRead = 0;
            byte[] buffer = new byte[100];
            do {
                bytesRead = r.ReadContentAsBase64(buffer, 0, buffer.Length);
                totalBytesRead += bytesRead;
                if (bytesRead > 0) {
                    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(
                        backToUIThread, totalBytesRead);
                }
            } while (bytesRead > 0);
            r.Close();
            mOut.Close();                
        }, null);
}

For me, one of the annoying parts of Silverlight is that it doesn't allow you to return a Stream directly, since Silverlight will turn it into a Byte[], even when using TransferMode.StreamedResponse. Using that technique it is possible to process something like a FileStream, in a streaming fashion, from a Silverlight client (see code below).

However, I would like to do something similar with really large C# POCOs. If I could receive a Stream I could use something like XML serialization on both ends and process the records as they were read. However, since the return value has to be a Byte[] I have to read to the end of the array before I can process it. This method does at least allow me to provide some feedback to the user.

I came up with two possibilities on possible solutions. One would be to write my own serializer, or at least deserializer, where I process data as it is being read (I am thinking delimited text). The other thought would be to do some type of variable bit Stream class, which I believe would require a steep learning curve on my part. I am looking for any ideas or links on possibilities on how to process values in a Byte[] as they are read.

Service Contract:

public static class Action {
    public static string val = "http://tempuri.org/stream";
}

// Share contract between client and server
[ServiceContract]
public interface IFileService {
#if SILVERLIGHT
    [OperationContract(AsyncPattern = true, Action = Action.val, ReplyAction = Action.val)]
    IAsyncResult BeginReadFile(Message request, AsyncCallback callback, object AsyncState);
    system.ServiceMode.Channels.Message EndReadFile(IAsyncResult result);
#else
    [OperationContract(AsyncPattern = true, Action = Action.val, ReplyAction = Action.val)]
    IAsyncResult BeginReadFile(string filePath, AsyncCallback callback, object AsyncState);
    system.IO.Stream EndReadFile(IAsyncResult result);
#endif
}

[DataContract(Name = "ReadFile", Namespace = "http://tempuri.org/")]
public class StreamRequest
{
    [DataMember(Order = 1)]
    public string FileName;
}

Service Implementation:

public IAsyncResult BeginReadFile(string filePath, AsyncCallback callback, object AsyncState) {
    return new CompletedAsyncResult<Stream>(File.OpenRead(filePath));
}

public Stream EndReadFile(IAsyncResult result) {
    return ((CompletedAsyncResult<Stream>)result).Data;
}

Client:

public void ReadFile( string filePath ) {
    BackToUIThread backToUIThread = byteCount => {
        this.OnBytesReceived(this, new AsyncEventArgs<object>(byteCount));
    };

    StreamRequest request = new StreamRequest() { FileName = "C:\\test.txt" };
    Message mIn = Message.CreateMessage(
        _myFactory.Endpoint.Binding.MessageVersion, Action.val, request);

    _myService.BeginReadFile(mIn,
        (asyncResult) =>
        {
            Message mOut = _myService.EndReadFile(asyncResult);
            System.Xml.XmlDictionaryReader r = mOut.GetReaderAtBodyContents();
            r.ReadToDescendant("ReadFileResult"); // move to stream
            r.Read(); // move to content

            int bytesRead = 1;
            long totalBytesRead = 0;
            byte[] buffer = new byte[100];
            do {
                bytesRead = r.ReadContentAsBase64(buffer, 0, buffer.Length);
                totalBytesRead += bytesRead;
                if (bytesRead > 0) {
                    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(
                        backToUIThread, totalBytesRead);
                }
            } while (bytesRead > 0);
            r.Close();
            mOut.Close();                
        }, null);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

反目相谮 2024-10-28 05:23:21

您可以让 Silverlight 应用程序将偏移量和要读取到服务的字节数传递给您。

您可以在 WCF 服务上使用类似

int offset = 0;  
int length = 1024;  
List<byte> bytes = new List<bytes>();  

_myService.ReadFileComplete += (s, e)=>  
{  
    bytes.Add(e.Result.Bytes);  
    if(asyncResult.Result.HasMore)  
    {  
        offset += length;  
        _myService.ReadFileAsync(fileName, offset, length);  
    }  
    else  
    {  
        DoSomethingWithBytes(bytes);  
    }  
}

_myService.ReadFileAsync(fileName, offset, length);

Then 的方法,只需使用带有给定偏移量和长度的 Stream.Read 方法即可。

您可以通过让服务返回要读取的下一个偏移量来改进这一点。

You could have your Silverlight app pass in the offset and the number of bytes your want read to your service.

You could have something like

int offset = 0;  
int length = 1024;  
List<byte> bytes = new List<bytes>();  

_myService.ReadFileComplete += (s, e)=>  
{  
    bytes.Add(e.Result.Bytes);  
    if(asyncResult.Result.HasMore)  
    {  
        offset += length;  
        _myService.ReadFileAsync(fileName, offset, length);  
    }  
    else  
    {  
        DoSomethingWithBytes(bytes);  
    }  
}

_myService.ReadFileAsync(fileName, offset, length);

Then on your WCF Service, just use the Stream.Read method with the given offset and length.

You could improve this by have the service return the next offset to be read.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文