Getting IEnumerable<T> semantics with a NetTcpBinding WCF service?

552 views Asked by At

First, this is not a duplicate of IEnumerable<T> as return type for WCF methods, I think I understand that the WCF architecture only allows concrete types to be transferred that can be stuffed into a message.

Second, our setup however is not a general service but connecting up a bundle of proprietary apps via C# + WCF + NetTcpBinding + Protobuf (only) so we may have more room for some tricks that something that needs to be more binding neutral.

Third, it is neither my place nor this question's to propose a different RPC or messaging framework.


"IEnumerable semantics", for the purpose of this question, are:

  • The returned sequence can be arbitrarily large -- it is therefore not possible to convert the sequence to a List or similar.
  • It is not known in advance how many items will be returned
  • Caller can just use foreach and be done with it.

In a local assembly, a C# interface my look like this:

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

You can't map that directly to a WCF service. Something that might achieve the same could look like:

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

Of course, using IStuffService will be more error prone than a IStuffProvider and add in to the mix than many usage scenarios would involve using both service and client on the same machine, so for "user code" it wouldn't be super important to be aware that "the network" is involved, the user code is just interested in a simple interface.

One option would be of course to have a client side interface wrapper implementation, that exposes IStuffProvider and internally forwards to and uses IStuffService. However, it seems it would really be desirable to not have to maintain two interfaces, one for user code, one solely for the WCF communication, especially as these applications are all tightly coupled anyway, so the additional abstraction just seems overhead.

What are the options we have here with WCF?


Note that after reading up on it, the Streamed Binding seems a poor solution, as I would still need a wrapper on the client side and the service interface would get more complex for no real gain in my case: I don't need maximum binary transfer efficiency, I would like good implementation + maintenance efficiency.

2

There are 2 answers

0
Martin Ba On BEST ANSWER

What I did in the end is have:

a) The OO interface IStuffProvideras above with the GetLines() member as above.

b) The WCF service interface (and it's implementation) implements an access pattern like this:

    [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) The client accesses the service through a proxy class that implements IStuffProvider and maps a call to the GetLines() function to the above three functions:

    // 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);
        }
    }
7
ie. On

Some time ago we faced the same WCF "restriction" in our project. To be short, we ended up with

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

Yes, it is not the same as IEnumerable<Stuff> GetItems(); Yes, we can get in troubles when some item added/removed on already received page. Yes, it is required some server-side tweaks, if server works with items in terms of IEnumerable<Stuff>. But it is still strictly typed and does not bring much additional logic in the client or the server.