0

Initially I had a LINQ query (say EF or any other) with expected deffered execution:

class Provider : IProvider
{
    public IEnumerable<Any> GetSome()
    {
         yield return new Any(); // LINQ query
    }
}

But now such a provider moved into a WCF service (and IoC):

unityContainer.RegisterType<IProvider>(
    new PerResolveLifetimeManager(),
    new InjectionFactory(c => new ChannelFactory<T>("IService").CreateChannel()));

Is it possible to preserve deferred execution over WCF call?

abatishchev
  • 92,232
  • 78
  • 284
  • 421
  • What do you expect to happen? Should the client request each item? Because that's what would need to happen to have deferred execution over the wire. – Daniel Hilgarth Feb 06 '13 at 08:31
  • @DanielHilgarth: Yes, I'm wondering is it possible to request each item turn by turn at the moment of materialization. Here's an example: I have some heavy EF call, WCF client has to wait it to complete, than serialize the whole result set, deserialize it back and only than start iterate over. Instead of start to process first results before last will be read from database/received from service. – abatishchev Feb 06 '13 at 08:35
  • That is about streaming - not about deferred execution ... And even with WCF streaming it will probably not work. You will have to go to very low level HTTP (chunked encoding) or TCP programing to achieve that. – Ladislav Mrnka Feb 06 '13 at 08:36
  • But that will introduce a lot of latency during the iteration, because for every item, a new request is sent to the server etc... I don't think it's the right approach. Did you measure locally what actually takes so long in the EF call? The time until the first item is returned? Or the iteration of all the items? – Daniel Hilgarth Feb 06 '13 at 08:38
  • 1
    @LadislavMrnka: I think those are two different concepts. Streaming delivers the data in a pace dictated by the server. Deferred execution delivers the data in a pace set by the client. – Daniel Hilgarth Feb 06 '13 at 08:39
  • @LadislavMrnka: Isn't such behavior as iteration over a sequence generated by for example 'yield return x' called deferred execution? An iteration in the initial data source takes place only on iteration in the final place? But maybe I'm just mixing terminology. – abatishchev Feb 06 '13 at 08:42
  • 1
    Yes that is. I have misunderstood your question. In such case it is exactly what @Daniel describes and you should simply expose something like `GetNext` on your service and wrap the enumeration in your client code (you will somehow have to track what is the next element). – Ladislav Mrnka Feb 06 '13 at 08:45
  • @DanielHilgarth: Long EF call long time caused not by some performance issue but by it's nature: this is a result of large text file parsing. So this is a very big set of small records. And I want to start process them before they all will be read first. My initial idea was to get it "for free" without additional latency generation as you fairly mentioned. – abatishchev Feb 06 '13 at 08:46
  • Well, you certainly won't get it for free, because every item will have the overhead of the request-response pair with its associated headers and latency. I agree with Ladislav, the best method would be to simply expose a `GetNext` method and wrap that in an enumerator on the client side. – Daniel Hilgarth Feb 06 '13 at 08:48
  • @LadislavMrnka: Okay, I see, so there are no "free donuts" possible. In LINQ to Any we have such behavior but a necessity to cross the service boundaries, i.e. data to be (de)serialized, breaks it, right? – abatishchev Feb 06 '13 at 08:50
  • @DanielHilgarth: Saying "for free" I mean to minimize code. I'm okay with additional network costs in exchange for start processing sooner. – abatishchev Feb 06 '13 at 08:51
  • Daniel, Ladislav, gentlemen, appreciate so much your advices! I understood, need to re-think, maybe re-design the approach I use currently. Thanks again! – abatishchev Feb 06 '13 at 08:52
  • @abatishchev: I provided a sketch implementation of what we talked about here in my answer below. It may help you in your decision. – Daniel Hilgarth Feb 06 '13 at 09:33

1 Answers1

1

This is answer is actually an answer to your last comment to Ladislav Mrnka. You say:

Okay, I see, so there are no "free donuts" possible. In LINQ to Any we have such behavior but a necessity to cross the service boundaries, i.e. data to be (de)serialized, breaks it, right?

While it doesn't come for free, it is still possible!

On the server side, you would have to provide a method to initialize the request and a method to get the results, one by one.

On the client side - specifically on one of its low level infrastructure classes - you can wrap it in an enumerator and finally, your "business" classes can use that enumerator just like any other.

We already discussed that it will introduce additional overhead in the means of the request-response needed for each item. This will introduce latency and increase the network load.

A sample of this approach using a pseudo RESTful API could look like this:

Server side:

  • POST http://server/api/search-specification:
    The body contains the parameters needed for your search, e.g. start date and end date
    The response will be an URI identifying the search-specification.
  • GET http://server/api/search-specification/1/next-result:
    The response will be the next item.

The controller for this looks something like this:

public Response PostSearchSpecification(SearchSpecification spec)
{
    int id = _searches.Max(x => x.Key) + 1; // Make thread-safe
    _searches[id] = _provider.GetSome().GetEnumerator();
    return ...;
}

public Item GetNextResult(int searchSpecId)
{
    if(_searches[searchSpecId].MoveNext())
        return _searches.Current;
    else
        return null; // or return a HTTP status code that tells the
                     // client that there are no more items.
}

I am calling it a pseudo RESTful API, because it certainly looks like one, but it needs to internally keep state for each specification to enable the deferred execution. Additionally GET http://server/api/search-specification/1/next-result is not idempotent.
But I think it demonstrates what I mean :)

The client side would encapsulate it somehow like this:

class Search
{
    public IEnumerable<Item> Start(params)
    {
        var client = new HttpClient(...);
        var resultsUri = client.Post(new SearchSpecification(params)).Response;
        Item item = client.Get<Item>(resultsUri);
        while(item != null)
        {
            yield return item;
            item = client.Get<Item>(resultsUri);
        }
    }
}

And you would use it like this:

var search = new Search();

foreach(var item in search.Start(...))
    // ...

Just a raw sketch on how you could implement something like this.

Daniel Hilgarth
  • 159,901
  • 39
  • 297
  • 411