【问题标题】:IEnumerable, streams and WCFIEnumerable、流和 WCF
【发布时间】:2018-03-18 00:30:21
【问题描述】:

我正在编写一个向客户端返回大量数据的服务。理想情况下,我想返回一个实体的 IEnumerable,因为我想要服务和客户端上的惰性带来的性能优势。我还希望能够压缩流以减少带宽。

我能够将 IEnumerable 序列化为流并利用 GZip 对其进行压缩。我还能够成功反序列化流。但是,我的实现并没有实现我的目标中的懒惰部分。

我已经阅读了与我的问题类似的概念的解决方案,但它们都涉及返回一个 IEnumerable 字节。理想情况下,我希望客户端接收 IEnumerable 的实体,并能够在反序列化时将其返回。

[DataContract]
public class Entity
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string Code { get; set; }
    [DataMember]
    public string Description { get; set; }
}

[Test]
public void TestSerialEnumGzip()
{
    var e = GetEnum();
    var s = SerializeToStreamGzip(e);
    Console.WriteLine($" TestSerialGzip stream size {s.Length}");
    var b = DeserializeFromStreamGzip<IEnumerable<Entity>>(s);
}

private IEnumerable<Entity> GetEnum()
{
    for (var x = 0; x < 10; ++x)
    {
        Console.WriteLine($"yielding {x}");
        yield return new Entity { Id = x, Code = x.ToString(), Description = x.ToString() };
    }
}

private Stream SerializeToStreamGzip<T>(T toSerialize)
{
    var s = new MemoryStream();
    using (var gz = new GZipStream(s, CompressionMode.Compress, true))
    {
        var ser = new DataContractSerializer(typeof(T));
        ser.WriteObject(gz, toSerialize);
    }
    s.Seek(0, SeekOrigin.Begin);
    return s;
}

private T DeserializeFromStreamGzip<T>(Stream stream)
{
    var ser = new DataContractSerializer(typeof(T));
    var gz = new GZipStream(stream, CompressionMode.Decompress);
    var result = (T)ser.ReadObject(gz);
    return result;
}

【问题讨论】:

  • 解释你想从懒惰中得到什么
  • However, my implementation doesn't achieve the Laziness part of my goal.你是怎么得出这个结论的?
  • @MichaelRandall 在服务器上,整个数据集可能需要一段时间才能收集,我希望能够尽快返回流。这样,即使服务可能尚未准备好所有数据,客户端也可以开始接收数据。这还有一个额外的好处,即服务不需要内存来打包整个数据集,
  • @mjwills 产生控制台写入发生在流大小写入之前。这意味着 IEnumerable 在客户端接收之前在服务上进行了迭代。
  • 您尝试做的事情实际上要复杂得多。而不是让SerializeToStreamGzip 在一个步骤中序列化来自e 的所有元素,您需要一个接一个地序列化它们,并以某种方式找到一种方法,只有在您的 Stream 的句柄返回到之后才能继续WCF。因此,MemoryStream 的实例(作为单个缓冲区,预先)是不够的,您必须返回自己的 Stream 实现,WCF 可以为此不断调用 Read 方法来获取后续块。我可能会首先寻找可以做到这一点的库

标签: c# wcf stream ienumerable gzipstream


【解决方案1】:

我认为您可能对 IEnumerable 感到有些困惑。但是,除此之外,您确实应该将研究重点放在 WCF 流媒体

查看此博客 Custom WCF Streaming 及其关联的 example。它基本上封装了你想要的一切,还使用了BinaryFormatter

如果您想更进一步,您可以使用 Protocol Buffer Protobuf-net 或添加您自己的 ad-hock 压缩。但是,我将这些细节留给你。

基本思路是:我们会有两个线程,一个线程来执行 复杂的数据库查询和另一个线程将数据库行流式传输到 客户。所以我们将改变数据库查询,使其返回 一次只有 1000 行。并修改 WCF 服务以流式传输这些 1000 行到客户端。当 WCF 服务将数据库行流式传输到 客户端,同时在不同的线程上,WCF 服务将 再次运行数据库查询以获取接下来的 1000 行。这种方式作为 一旦 WCF 服务完成将行流式传输到客户端, 下一组行可用于流式传输到客户端

  1. WCF 客户端调用 WCF 服务
  2. WCF 服务执行数据库查询
  3. 数据库将数据集返回给 WCF 服务
  4. WCF 服务响应
  5. WCF 服务执行的第二个数据库查询
  6. WCF 流响应

【讨论】:

  • 谢谢。这不是我想要的,但它可以帮助我到达我想去的地方。
  • @PaulTsai 不幸的是,你想做的事情不能开箱即用,而且我知道真的没有图书馆可以做到这一点。但是,正如您所指出的,该示例基本上是该问题的常见解决方案,尽管我认为另一种更稳定的解决方案是一次要求 1000 条记录并计算剩余的数量,当客户端完成处理时,它只要求接下来的 1000 个。你会惊讶于它的效率。无论如何,我认为您现在已经足够了解问题和潜在的解决方案了。你也可以在这上面放一个赏金,它可能会拖得更多
猜你喜欢
  • 2013-08-14
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多