views:

728

answers:

4

I am writing refactoring a Silverlight program to consumes a portion of its existing business logic from a WCF service. In doing so, I've run into the restriction in Silverlight 3 that only allows asynchronous calls to WCF services to avoid cases where long-running or non-responsive service calls block the UI thread (SL has an interesting queuing model for invoking WCF services on the UI thread).

As a consequence, writing what once was straightforward, is becoming rapidly more complex (see the code examples at the end of my question).

Ideally, I would use coroutines to simplify the implementation, but sadly, C# does not currently support coroutines as a native language facility. However, C# does have the concept of generators (iterators) using the yield return syntax. My idea is to re-purpose the yield keyword to allow me to build a simple coroutine model for the same logic.

I am reluctant to do this, however, because I am worried that there may be some hidden (technical) pitfalls that I'm not anticipating (given my relative inexperience with Silverlight and WCF). I am also worried that the implementation mechanism may not be clear to future developers and may hinder rather than simplify their efforts to maintain or extend the code in the future. I've seen this question on SO about re-purposing iterators to build state machines: http://stackoverflow.com/questions/1194853/implementing-a-state-machine-using-the-yield-keyword, and while it's not exactly the same thing I'm doing, it does make me pause.

However, I need to do something to hide the complexity of the service calls and manage the effort and potential risk of defects in this type of change. I am open to other ideas or approaches I can use to solve this problem.

The original non-WCF version of the code looks something like this:

void Button_Clicked( object sender, EventArgs e ) {
   using( var bizLogic = new BusinessLogicLayer() ) {
       try  {
           var resultFoo = bizLogic.Foo();
           // ... do something with resultFoo and the UI
           var resultBar = bizLogic.Bar(resultFoo);
           // ... do something with resultBar and the UI
           var resultBaz = bizLogic.Baz(resultBar);
           // ... do something with resultFoo, resultBar, resultBaz
       }
   }
}

The re-factored WCF version becomes quite a bit more involved (even without exception handling and pre/post condition testing):

// fields needed to manage distributed/async state
private FooResponse m_ResultFoo;  
private BarResponse m_ResultBar;
private BazResponse m_ResultBaz;
private SomeServiceClient m_Service;

void Button_Clicked( object sender, EventArgs e ) {
    this.IsEnabled = false; // disable the UI while processing async WECF call chain
    m_Service = new SomeServiceClient();
    m_Service.FooCompleted += OnFooCompleted;
    m_Service.BeginFoo();
}

// called asynchronously by SL when service responds
void OnFooCompleted( FooResponse fr ) {
    m_ResultFoo = fr.Response;
    // do some UI processing with resultFoo
    m_Service.BarCompleted += OnBarCompleted;
    m_Service.BeginBar();
}

void OnBarCompleted( BarResponse br ) {
    m_ResultBar = br.Response;
    // do some processing with resultBar
    m_Service.BazCompleted += OnBazCompleted;
    m_Service.BeginBaz();
} 

void OnBazCompleted( BazResponse bz ) {
    m_ResultBaz = bz.Response;
    // ... do some processing with Foo/Bar/Baz results
    m_Service.Dispose();
}

The above code is obviously a simplification, in that it omits exception handling, nullity checks, and other practices that would be necessary in production code. Nonetheless, I think it demonstrates the rapid increase in complexity that begins to occur with the asynchronous WCF programming model in Silverlight. Re-factoring the original implementation (which didn't use a service layer, but rather had its logic embedded in the SL client) is rapidly looking to be a daunting task. And one that is likely to be quite error prone.

The co-routine version of the code would look something like this (I have not tested this yet):

void Button_Clicked( object sender, EventArgs e ) {
    PerformSteps( ButtonClickCoRoutine );
}

private IEnumerable<Action> ButtonClickCoRoutine() {
    using( var service = new SomeServiceClient() ) {
        FooResponse resultFoo;
        BarResponse resultBar;
        BazResponse resultBaz;

        yield return () => {
            service.FooCompleted = r => NextStep( r, out resultFoo );
            service.BeginFoo();
        };
        yield return () => {
            // do some UI stuff with resultFoo
            service.BarCompleted = r => NextStep( r, out resultBar );
            service.BeginBar();
        };
        yield return () => {
            // do some UI stuff with resultBar
            service.BazCompleted = r => NextStep( r, out resultBaz );
            service.BeginBaz();
        };
        yield return () => {
            // do some processing with resultFoo, resultBar, resultBaz
        }
    }
}

private void NextStep<T>( T result, out T store ) { 
    store = result;
    PerformSteps();  // continues iterating steps
}

private IEnumerable<Action> m_StepsToPerform;
private void PerformSteps( IEnumerable<Action> steps ) {
   m_StepsToPerform = steps;
   PerformSteps();        
}

private void PerformSteps() {
   if( m_StepsToPerform == null ) 
       return; // nothing to do

   m_StepsToPerform.MoveNext();
   var nextStep = m_StepsToPerform.Current;
   if( nextStep == null ) {
       m_StepsToPerform.Dispose();
       m_StepsToPerform = null;
       return; // end of steps
   }
   nextStep();
}

There are all sorts of things that need to be improved in the above code. But the basic premise is to factor out the continuation pattern (creating an interception point for exception handling and various checks) while allowing the event-based async model of WCF to drive when each step is performed - basically when the last async WCF call completes. While on the surface this looks like more code, it's worth mentioning that PerformSteps() and NextStep() are reusable, only the implementation in ButtonClickCoRoutine() would change with each different implementation site.

I'm not entirely sure I like this model, and I wouldn't be surprised if a simpler way existed to implement it. But I haven't been able to find one on the "interwebs" or MSDN, or anywhere else. Thanks in advance for the help.

+6  A: 

You should definitely look at the Coordination and Concurrency Runtime. It uses iterators for exactly this purpose.

On the other hand, you should also look at Parallel Extensions and its approach to continuations. Parallel Extensions is part of .NET 4.0, whereas the CCR requires separate licensing. I would advise you to go with a framework written by people who eat, breathe and sleep this stuff though. It's just too easy to get details wrong on your own.

Jon Skeet
I agree. My understanding was that the CCR had certain licensing restrictions (which if I understand them correctly will make it impossible for me to use). The Parallel Extensions library and .NET 4.0 is currently in preview - I have looked at them but I don't see any classes or facilities to do this sort of thing. Was there a particular part of PTE you had in mind?
LBushkin
LBushkin: You can get the Parallel Extensions backported as part of the Reactive Extensions (see my post for link). There is a Task.FromAsync method (+overloads) that let you construct a task from an IAsyncResult-based method pair, as well as using Task.ContinueWith for continuations.
Reed Copsey
Reed: Thanks. I'll take a look at that.
LBushkin
The CCR is now a free download as part of the newly released Robotics studio R3
Harry
+3  A: 

The Reactive Extensions for .NET provide a much cleaner model for handling this.

They provide extensions which let you write simple delegates against asynchronous events in a much, much cleaner manner. I recommend looking into them, and adapting them to this situation.

Reed Copsey
I have looked at Rx.NET, however it's in preview itself and probably won't be available for my timeframes. I'm also unclear as to exactly how to write this type of code RX.NET - I've seen examples that illustrate treating event sources as IObservable<> - essentially infinite iterators, but not how to structure this pattern. Are you aware of any examples I could look at?
LBushkin
Take a look at this: http://themechanicalbride.blogspot.com/2009/07/developing-with-rx-part-2-converting.html Requires a small wrapping library, but would be easy to extend this with continuations. THere is, however, quite a few samples out there of making tasks from observables with Rx.
Reed Copsey
+1 RX is the direction to take this sort of thing in Silverlight. I shouldn't worry too much about the "Preview" status of Rx, if that were really a concern perhaps Silverlight as whole isn't for you.
AnthonyWJones
+1  A: 

I didn't read your whole thing.

They use this strategy in CCR robotics studio, and a number of other projects use this strategy. An alternative is to use LINQ, see e.g. this blog for a description. The Reactive framework (Rx) is kinda built along these lines.

Luca mentions in his PDC talk that perhaps future version of C#/VB may add async primitives to the language.

In the meantime, if you can use F#, it is a winning strategy. Right now what you can do with F# here blows everything else right out of the water.

EDIT

To quote the example from my blog, suppose you have a WCF client that you want to call a couple methods on. The synchronous version might be written as

// a sample client function that runs synchronously 
let SumSquares (client : IMyClientContract) = 
    (box client :?> IClientChannel).Open() 
    let sq1 = client.Square(3) 
    let sq2 = client.Square(4) 
    (box client :?> IClientChannel).Close() 
    sq1 + sq2

and the corresponding async code would be

// async version of our sample client - does not hold threads 
// while calling out to network 
let SumSquaresAsync (client : IMyClientContract) = 
    async { do! (box client :?> IClientChannel).OpenAsync() 
            let! sq1 = client.SquareAsync(3) 
            let! sq2 = client.SquareAsync(4) 
            do! (box client :?> IClientChannel).CloseAsync() 
            return sq1 + sq2 }

No crazy callbacks, you can use control constructs like if-then-else, while, try-finally, etc, write it almost exactly like you write straight-line code, and everything work, but now it's async. It's very easy to take a given BeginFoo/EndFoo pair of methods and make the corresponding F# async methods for use in this model.

Brian
As I responded to Reed and Jon, I've looked at CCR, Parallel Extensions, and Reactive.NET - however I haven't seen a good example that I can relate to what I'm doing. That would go a long way in helping me decide which (if any) of these libraries could fit the bill.
LBushkin
Do you have an F# example I could look at? I don't think that I can use F# in a Silverlight 3 app, but I may be able to learn something from the approach it uses.
LBushkin
Check out Luca's PDC talk from 2008 here: http://channel9.msdn.com/pdc2008/TL11/In the video, watch about 10 minutes starting at 50 minutes in, he shows how easy it is to make async calls in F#.You can use an F# library in a Silverlight 3 app.
Brian
A: 

You may also want to consider Jeffrey Richter's AsyncEnumerator which is part of his 'power threading' library. He worked together with the CCR team to develop CCR. The AsyncEnumerator is according to Jeffrey more 'lightweight' than the CCR. Personally I've played about with AsyncEnumerator but not with the CCR.

I haven't used it in anger though - so far I've found the limitations with using enumerators to implement coroutines too painful. Currently learning F# because of amongst other things async workflows (If I remember the name correctly) which look like they're full fledged coroutines or 'continuations' (I forget the correct name or the exact distinctions between the terms).

Anyway, here's some links:

http://www.wintellect.com/PowerThreading.aspx

Channel 9 video on AsyncEnumerator

MSDN Article

Phil