Click here to Skip to main content
Click here to Skip to main content

Yield, Web Continuations!

By , 7 Aug 2006
Rate this:
Please Sign up or sign in to vote.

Introduction

The article shows how to emulate web continuation in .NET framework. It makes the creation of complex flow in web applications possible.

Background

A continuation is a representation of the execution state of a program at a certain point. Not all languages can support continuations. Saving of the state in web application gives additional difficulty.

Iterator block was introduced in C# 2.0. It can be presented as a method body that returns one of the generic enumerator (enumerable) interfaces and contains at least one yield statement. Following example shows how iterator block is working. The function saves all internal variables and returns control to caller on yield return statement; the function restores state and resumes execution when another value is requested.

        static IEnumerable<int> Fibonacci(int n)
        {
            int previous = 0;
            int current = 1;
            for (int i = 0; i < n; i++)
            {
                yield return current;
                int next = previous + current;
                previous = current;
                current = next;                
            }
        }
        ...
        // Prints 1 1 2 3 5 8 13 21 34 55
        foreach (int i in Fibonacci(10))  Console.WriteLine(i);

The enumerator saves local variables and execution resume point in memory, but it doesn’t allow exporting and importing of the state to and from an external stream. It’s the reason why it cannot be used as true web continuation solution.

Using the code

Web continuation manager implemented as ContinuationManager server control for .NET framework (v2.0.50727). The ASPX page that uses continuation has to contain this control.

As example, we will implement number guessing game using natural execution flow. Let’s declare iterator block inside page code behind class:

    private IEnumerator<object> NumberGuess()
    {       
        Random random = new Random(); // generate random number
        int answer = random.Next(1, 101);
        int attempts = 1;

        mainMultiView.SetActiveView(guessView);
        lastGuessPanel.Visible = false;
        yield return null; // store and return control

        while(guess != answer)
        {
            ++attempts;
            lastGuessPanel.Visible = true;
            lastGuessLabel.Text = guess.ToString();

            lowerLabel.Visible = guess < answer;
            higherLabel.Visible = guess > answer;
            yield return null; // store and return control
        }

        mainMultiView.SetActiveView(resultView);
        guessedNumberLabel.Text = answer.ToString();
        attemptsLabel.Text = attempts.ToString();
    }

We have to register enumerator with ContinuationManager control and then make first call.

    controlManager.Register(NumberGuess());
    controlManager.Call();

It will update continuation data in ViewState container and return control immediately after first yield return statement. After that page will be rendered and submitted to the browser.

Execution flow using web continuation

When next request is being processed continuation will restore itself form ViewState and all you need to do is make another call to continue execution of the interrupted code. Since enumerator instance refers to current page the user interface can be changed to display new input controls or messages.

Points of Interest

Full description of C# 2.0 “unfolding” of iterator block into enumerator can be found in “C# 2.0 Specification”. We can notice that the enumerator contains all the local variables, pointer to class that declares that method (unless the method is static), state and current returned value. The enumerator type is declared as private nested class. The enumerator data can be imported and exported using System.Reflection classes. (see implementation of Continuation<T> class)

ContinuationManager server control should be located on the control that declares iteration block. That helps to automatically find the owner control of the restored enumerator.

Special attention is required for objects that implements IDisposable interface. Usually a IDisposable object isn’t marked as Serializable therefore it is impossible to import/export its state. However the objects can be used between yield statements.

License

This article, along with any associated source code and files, is licensed under The MIT License

About the Author

notmasteryet
Software Developer
United States United States
No Biography provided
Follow on   Twitter

Comments and Discussions

 
GeneralVery Interesting PinmemberJeffster30-Aug-07 9:30 
GeneralRe: Very Interesting Pinmembernotmasteryet6-Oct-07 5:54 
GeneralSample code Pinmembergonser8-Aug-06 10:41 
AnswerRe: Sample code Pinmembernotmasteryet8-Aug-06 12:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 7 Aug 2006
Article Copyright 2006 by notmasteryet
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid