使用NetTcpBinding WCF服务获取IEnumerable <T>语义?

Mar*_* Ba 7 c# wcf remoteobject protobuf-net nettcpbinding

首先,这不是IEnumerable <T>的副本作为WCF方法的返回类型,我想我明白WCF架构只允许传输可以填充到消息中的具体类型.

其次,我们的设置不是一般服务,而是通过C#+ WCF + NetTcpBinding + Protobuf(仅)连接一组专有应用程序,因此我们可能有更多空间来处理某些需要更具约束力中性的技巧.

第三,提出不同的RPC或消息框架既不是我的地方,也不是这个问题.


出于这个问题的目的,"IEnumerable语义"是:

  • 返回的顺序可以任意大-因此它是不是可以将序列转换为List或相似.
  • 这是不是事先知道有多少项目会被退回
  • 来电者可以使用foreach并完成它.

在本地程序集中,C#接口看起来像这样:

interface IStuffProvider {
  IEnumerable<Stuff> GetItems(); // may open large file or access database
}
Run Code Online (Sandbox Code Playgroud)

您无法将其直接映射到WCF服务.可能达到相同效果的东西可能如下所示:

[ServiceContract(SessionMode = SessionMode.Required)]
interface IStuffService {
  [OperationContract]
  void Reset(); // may open large file or access database
  [OperationContract]
  List<Stuff> GetNext(); // return next batch of items (empty list if no more available)
}
Run Code Online (Sandbox Code Playgroud)

当然,使用IStuffService将比a IStuffProvider和添加到混合中更容易出错,而不是许多使用场景将涉及在同一台机器上同时使用服务和客户端,因此对于"用户代码",要意识到这一点并不是非常重要的.涉及"网络",用户代码只对简单的界面感兴趣.

一种选择当然是拥有一个客户端接口包装器实现,它公开IStuffProvider并内部转发和使用IStuffService.然而,现在看来,这真的希望没有必须保持两个接口,一个用于用户代码,一个纯粹为WCF通信,特别是因为这些应用程序都是严格反正耦合的,因此额外的抽象似乎只是开销.

我们在这里使用WCF有哪些选择?


请注意,在阅读之后,Streamed Binding似乎是一个糟糕的解决方案,因为我仍然需要客户端上的包装器,并且服务接口会变得更加复杂,在我的情况下没有真正的好处:我不需要最大二进制转移效率,我想好的实施+维护效率.

Mar*_* Ba 0

我最终所做的是:

a) OO接口 IStuffProvider如上,成员GetLines()如上。

b) WCF 服务接口(及其实现)实现如下访问模式:

    [OperationContract]
    ReadToken StartReadingLines(...);

    [OperationContract]
    // return next batch of items (empty list if no more available)
    List<Stuff> ReadNextLines(ReadToken readToken);

    [OperationContract]
    void FinishReadingLines(ReadToken readToken);
Run Code Online (Sandbox Code Playgroud)

c) 客户端通过代理类访问服务,该代理类实现IStuffProvider并对函数的调用映射GetLines()到上述三个函数:

    // implementation in the proxy class:
    public IEnumerable<Stuff> GetLines()
    {
        var readToken = _dataService.StartReadingLines(...);
        try {
            for (List<Stuff> lines = _dataService.ReadNextLines(readToken); lines.Count > 0; lines = _dataService.ReadNextLines(readToken)) {
                foreach (var line in lines) {
                    yield return line;
                }
            }
        } finally {
            _dataService.FinishReadingLines(readToken);
        }
    }
Run Code Online (Sandbox Code Playgroud)