使用 NetTcpBinding WCF 服务获取 IEnumerable 语义

本文关键字:语义 IEnumerable 获取 NetTcpBinding WCF 服务 使用 | 更新日期: 2023-09-27 18:30:38

首先,这不是 IEnumerable 作为 WCF 方法返回类型的重复,我想我理解 WCF 体系结构只允许传输可以填充到消息中的具体类型。

其次,我们的设置不是一般服务,而是通过 C# + WCF + NetTcpBinding + Protobuf(仅)连接一系列专有应用程序,因此我们可能有更多的空间来使用一些技巧,这些技巧需要更加绑定中立。

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


就此问题而言,"IEnumerable 语义"是:

  • 返回的序列可以是任意大的 - 因此无法将序列转换为List或类似序列。
  • 事先不知道将退回多少物品
  • 调用方只需使用foreach即可完成。

在本地程序集中,C# 接口如下所示:

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

不能将其直接映射到 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)
}

当然,使用IStuffServiceIStuffProvider更容易出错,并且比许多使用场景涉及在同一台机器上使用服务和客户端更易于添加,因此对于"用户代码"来说,意识到涉及"网络"并不是非常重要,用户代码只是对一个简单的界面感兴趣。

一种选择当然是有一个客户端接口包装器实现,它公开IStuffProvider并在内部转发和使用IStuffService。但是,似乎确实不希望维护两个接口,一个用于用户代码,一个仅用于 WCF 通信,特别是因为这些应用程序无论如何都是紧密耦合的,因此额外的抽象似乎只是开销。

我们在WCF方面有哪些选择?


请注意,在阅读它之后,流绑定似乎是一个糟糕的解决方案,因为我仍然需要在客户端使用包装器,并且服务接口会变得更加复杂,而在我的情况下没有真正的收益:我不需要最大的二进制传输效率,我想要良好的实现 + 维护效率。

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

前段时间,我们在项目中遇到了同样的WCF"限制"。简而言之,我们最终得到了

interface IStuffProvider {
  List<Stuff> GetItems(int page, int pageSize); // may open large file or access database
}

是的,它与IEnumerable<Stuff> GetItems();不同 是的,当在已接收的页面上添加/删除某些项目时,我们可能会遇到麻烦。是的,如果服务器在IEnumerable<Stuff>方面处理项目,则需要进行一些服务器端调整。但它仍然是严格类型的,不会在客户端或服务器中带来太多额外的逻辑。

我最后做的是:

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);

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);
        }
    }