ASP.NET Routing in Windows Azure Using WebForms

Wednesday, May 27, 2009 1:53:18 PM (Pacific Standard Time, UTC-08:00)

I'm a huge fan of ASP.NET Routing. It gained popularity as the part of ASP.NET MVC which channels requests for a given URL to the right controller action. In a wise move, Microsoft moved the routing infrastructure out of ASP.NET MVC and into its own assembly with the release of .NET 3.5 SP1.

With ASP.NET Routing you can construct search engine optimized and human friendly URLs such as these:


Here part of the URL (tag or user) selects the page and part of the URL (everything or codinghorror) are effectively query parameters to the page.

This is well documented in the ASP.NET MVC world running on your server - you can't get anything done without it in MVC. But what about Windows Azure? What if you don't want ASP.NET MVC? What if you're a traditional type of person and want all the goodness that comes with what is now called ASP.NET WebForms (aka "normal ASP.NET")?

In this brief post, I'll cover how to use ASP.NET routing and ASP.NET WebForms in Azure. The sample project can be downloaded if you want to follow along. Phil Haack has written a good post on using routing alongside ASP.NET WebForms so I won't cover too much background information.


How does this change for Azure?

The short answer is that it doesn't. If you get routing working for IIS 7 in your web app, you can effectively deploy it to Azure. But the steps always felt convoluted to me when reading others' write-ups on this. So let's run through converting a Windows Azure Web Role (essentially a "stock" ASP.NET WebForms app) to use routing in Azure.

First you'll need the Azure SDK and Visual Studio tools:


  1. Next, create a new solution in Visual Studio by choosing Cloud Service->Web and Worker Cloud Service.

  2. Add a new Global.asax file to your web role project.

  3. Add a reference to System.Web.Routing and System.Web.Abstractions in your web role project.

  4. Define a custom class that derives from IRouteHandler which will map URL parameters into the HttpContext for use in your pages:
    internal class CustomRoute : IRouteHandler
    {
        public CustomRoute(string virtualPath)
        {
            VirtualPath = virtualPath;
        }
    
        public string VirtualPath { get; private set; }
    
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            foreach (var aux in requestContext.RouteData.Values)
            {
                HttpContext.Current.Items[aux.Key] = aux.Value;
            }
    
            return BuildManager.CreateInstanceFromVirtualPath(
                       VirtualPath, typeof (Page)) as IHttpHandler;
        }
    }
    
  5. Register these routes in the Application_Start method of your Global.asax:
    protected void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.Add( "ShowName",
               new Route(
                "naming/show/{name}",
                new CustomRoute( "~/ShowName.aspx" )
                ) );
    
        RouteTable.Routes.Add( "CreateAccount",
               new Route(
                "account/begin",
                new CustomRoute( "~/Account.aspx" )
                ) );
    
        RouteTable.Routes.Add( "Home",
               new Route(
                "home",
                new CustomRoute( "~/Default.aspx" )
                ) );
    }
    
    Now if you run your app, you might expect the routing infrastructure to work. Inside the ASP.NET Dev Server (aka cassini) this will likely work. But in the Azure Development Fabric you'll see this:



    The problem is you need to tell IIS 7.5 to get out of the way and let the request get to ASP.NET.

  6. We'll define a class to short-circuit the IIS validation
    class Iis7RoutingHandler : UrlRoutingHandler
    {
        protected override void VerifyAndProcessRequest(
            IHttpHandler httpHandler, HttpContextBase httpContext)
        {
        }
    }
    
  7. Modify the web.config by adding a handler and module to the system.webServer section:
    <system.webServer>
       <modules runAllManagedModulesForAllRequests="true">
       ...
          <add name="UrlRoutingModule" 
             type="System.Web.Routing.UrlRoutingModule, 
             System.Web.Routing, Version=3.5.0.0, 
             Culture=neutral, 
             PublicKeyToken=31BF3856AD364E35" />
       </modules>
       <handlers>
          <add 
             name="UrlRoutingHandler" 
             preCondition="integratedMode" 
             verb="*" 
             path="UrlRouting.axd" 
             type="WebCore.Iis7RoutingHandler, WebCore"/>
       </handlers>
     </system.webServer>
    
  8. Finally, we need to recover the data passed to the page. For example, in the sample project we have:

    route: /naming/show/{name}
    example: /naming/show/michael-kennedy

    How will our page access the value of name? Recall that our custom route stashes the values in HttpContext.Current.Items. We'll just pull them back out as follows in our Page_Load method of our ASPX class:
    LabelName.Text = (string)HttpContext.Current.Items["name"];
That's it! You can see our routes working in our WebForms app running in Azure (well, technically the screenshot is the dev fabric - but it works in the cloud as well):

Download the source and try it for yourself: AzureRoutingSample.zip (136 KB)

Article: Azure Storage

Wednesday, April 08, 2009 12:03:23 PM (Pacific Standard Time, UTC-08:00)

I recently wrote an article for DevelopMentor's Developments newsletter entitled Azure Storage. Read it at the DevelopMentor website here:

http://www.develop.com/content/newsletters/aprilazure

I've republished here for my readers. Enjoy!


Developments: Azure Storage

by Michael Kennedy

[Listen to this article as a podcast: Azure-Storage-Article-Kennedy.mp3]

October 27th 2008, Los Angeles CA - It's 9 AM and Microsoft is hosting PDC (their most forward looking developer conference). Ray Ozzie and company are introducing Windows Azure: A new platform which is their first foray into the nascent world of large-scale utility computing. This scalable and reliable platform-as-a-service functionality is commonly referred to as "Cloud Computing" because it runs somewhere out there on the Internet.

Computing platforms that rival the reliability of the utility grids (e.g. electric and gas) which we daily take for granted have long been the stuff of dreams.

A few companies have realized this dream - Google and Amazon come to mind as a couple of the rare exceptions who have accomplished this goal. These companies' web properties seem to handle unbounded amounts of traffic with zero down time. The data centers, redundancies, software engineering and operations know-how required to make this happen are exceedingly expensive. Some reports have Google spending over $2.4 billion (that's 2,400 million dollars) on data centers in 2007 alone.

Prior to large-scale cloud computing efforts (circa 2005), most of us could only dream of such scalability and reliability. Today we have at least three highly reputable companies offering some kind of pay as you go cloud computing platform - Microsoft, Amazon, and Google.

Microsoft's Azure is a new comer to the industry. But for .NET developers, it is not to be ignored. Azure allows you to use your existing skills to build essentially the same .NET applications you are familiar with and "deploy them to the cloud."

These scalable, reliable, and geographically-replicated applications that run on Azure depend on data of course. Virtually all applications we write will be nothing without their underlying data. But if we simply use the tried and true methods of data storage such as the file system or a (single) database server our data is not all that scalable or reliable. Because we cannot have a scalable and reliable application without data, we need a new mechanism for storing and accessing data from our Azure applications.

Enter Azure Storage

Azure storage is the storage component of the Azure platform. It is actually three data services in one:

  • Blob Storage - stores unstructured data essentially as a file, limited to 50 GB of data per blob.
  • Table Storage - stores structured data that is somewhat like a database. For full database capabilities there is a high level feature called Sql Data Services (SDS).
  • Queues - provides interprocess communication functionality between various web and worker roles in your hosted services or even applications running outside of Azure. Queues can pass small xml or binary messages - less than 64 kb per message.

In this article, we will cover just the basics of the three storage services of Windows Azure. I want to give you a sense for what it's like to program against Azure Storage. At the base level all access to Azure Storage uses pure REST APIs. This means that you can access it from any HTTP enabled platform / language. For example, to download the blob data called "config.xml" in the container called "settings" for the Azure project "kennedy" you would simply issue a GET to the Uri:

   http://kennedy.blob.core.windows.net/settings/config.xml

To save data in a blob you do HTTP POSTs and PUTs in a similar fashion. However, real life is full of edge cases, error handling, security, and serialization which makes the pure HTTP model error prone. Thus, a sample library serves as the de facto .NET API to Azure Storage and ships with the Azure SDK. It is called StorageClient and can be found in default installs here:

   C:\Program Files\Windows Azure SDK\v1.0\samples\StorageClient

We will examine working with each of the storage services from the perspective of the StorageClient library - but keep in mind that ultimately this library is a wrapper around a basic and open RESTful API.

Setting The Stage: The Sample Application

To explore Azure Storage I have written a simple photo sharing distributed application. These set of applications allow users to upload photos to a photo sharing site. These photos must be reviewed and approved by moderators of the site. Once approved, the general public can view and interact with the photos. For a concrete example, you could imagine writing a distributed version of the wallpaper sharing site InterfaceLift and deploying it on Azure in this fashion.

You can download the sample application and follow along if you want to see the full source code and try it out yourself. Just be sure to start the Development Storage utility that comes with the Azure SDK before running the application.

Our distributed application consists of three parts.

  • The Uploader: A Windows Forms application that lets contributors upload images to the site.
  • The Reviewer: A Windows Forms application that lets moderators view image submissions and either approve or reject them.
  • The Website: An ASP.NET website for viewing the photos - this is our public facing application.

A typical use case might be as follows (see diagram below).

  1. We upload a photo submission with our uploader application. The photo is uploaded to Azure blob storage and a message is sent via an Azure Message Queue to all available reviewer applications. Additional information about the submitter is associated with the photo in Azure table storage.
  2. The reviewer application watches the message queue for new messages. When one arrives, the photo is added to a list of pending submissions. The reviewer can either reject (delete) the submission or approve it - move it to a permanent blob storage location where it will be publicly viewable.
  3. Users visit our website and can view all approved photos. This list will change in real-time because it is driven by the reviewer application. The web application simply pulls all photos from the approved photo container in Azure blob storage.

Saving Data: Creating Azure Blobs

To save data to Azure Blob Storage, you must realize blob storage follows the ACE pattern (Authority, Container, Entity) to describe a blob. Authority is simply your Azure solution name. Containers are analogous to folders. And entities are analogous to files.

The listing below is essentially the code that runs when the uploader application uploads a pending image submission to blob storage.

Listing 1:

Sending Notifications: Azure Queuing

In addition to uploading the image to the pending images container in blob storage, we will send a message to a message queue to notify any active or future reviewers of the new submission.

Listing 2.

Saving (More) Data: Structured Storage and Azure Tables

Finally, for the upload application, we must also save some information about the contributor. In Azure Storage we have two reasonable places to store this information.

First, we could save this information directly in blob storage as meta-data associated with the blob itself. This is straightforward and easy. But there is a big limitation: information in this meta-data is not queryable. Suppose I want get all images associated with a single contributor. There is no way in Azure Blob Storage to say give me all the blobs with this filter on the meta-data. You would have to pull the properties of every blob and do the comparison client-side. That's tantamount to filling a DataSet with "SELECT * FROM PendingImages" and it's a bad idea.

Instead we will use the third type of Azure Storage: Azure Table Storage. Table Storage allows us to store data with up to 256 properties and query this data as if it were a database. It is exactly what we need for the contributor information. However, you must realize this is not a database. A better mental picture is a durable collection of Dictionary object (as in Dictionary from System.Collections.Generics) with querying built on top. I say this because there is no schema or relational constructs in Azure Table Storage. If you need that, then you'll want Sql Data Services - a service on top of the core Azure platform.

The code to add an entry to Azure Table Storage does not fit into a single method as it's driven through the interaction of several classes we must define. Azure Table Storage can be accessed via ADO.NET Data Services (client-side) and this is the method we will use.

First we'll define a client-side schema for our entry by creating a class called Contributor which derives from the class TableStorageEntity (from the StorageClient library).

Listing 3.

Additionally we must define the tables and queries available to ADO.NET Data Services by created a class derived from TableStorageDataServiceContext and we do that below. We simply have one table called Contributors.

Listing 4.

With those two items in place, we can insert a "row" into Azure Table Storage as follows:

Listing 5.

As for querying Azure Table Storage that is very straight-forward. Because we are using ADO.NET Data Services, querying can be done via LINQ as in "from c in svc.Contributors select c.Name". Ultimately ADO.NET Data Services is also built on a RESTful API so this translates to the underlying HTTP REST calls. Alternatively, you can use that REST API directly from .NET or any other platform.

Waiting on Queues: The Reviewer Application's Code

Next, let's look at how we monitor and pull messages from Azure Queuing. Ultimately we must poll the queue using a RESTful HTTP request. But the StorageClient resurfaces this to us as simple events.

Listing 6.

We won't cover how we move a blob from the pendingImages blob container to the approvedImages blob container which happens when a reviewer approves an image. You can look at the sample to see how that is done.

Ultimately It's About the Website

Finally, let's look at the web application that actually displays the approved images. We don't do anything fancy such as paging or error handling that you'd see in a real application. But this will give you a good idea how to work with the blob data as a collection.

Here we'll create a BlobStorage object and access the BlobContainer approvedImageContainer as we have been in most of the listings. But then instead of saving or reading blobs, we use the ListBlobs method to simply list all the approve images in that container. In order to show the images on our webpage, we just use the BlobProperties.Uri and directly reference that in our HTML. Our ASP.NET application does not touch the data. Rather the consumers (IE, Firefox, Chrome, etc) of the HTML pull the image data directly from blob storage as they would from any web server.

Listing 7.

Now you have a good idea of the concepts and motivation behind Azure Storage. You have seen some typical usages of each of the three storage features: blob storage, table storage, and queuing. Our samples made use of the sample storage API library called StorageClient. Underlying this library we saw that Azure Storage is entirely accessed via RESTful APIs.

Want to get started? Visit http://www.azure.com and choose "Try It Now" to register for a CTP Azure account. You'll need to download the various SDK's listed on that same page. They will install the Visual Studio projects required for working with Azure as well as the Development Storage and Development Fabric so you can develop and debug your applications before deploying them to the cloud.

If you want some intensive, expert-lead training on Azure and associated .NET 4.0 topics be sure to contact DevelopMentor. Or call 800.699.1932 to find out what classes we have available today.

RESTful Web Services with WCF Screencast

Tuesday, March 24, 2009 11:02:03 AM (Pacific Standard Time, UTC-08:00)

I recently got the chance to record a screencast discussing REST-oriented web services in WCF. If you're interested in WCF you should definitely check it out because WCF and REST make an awesome combination. kennedy-wcf-rest-video-screenshot.png
   WCF-REST-Kennedy-Peepleocity.wmv 35 MB (WMV HD)

I cover building WCF services using REST princples, the WebGet and WebInvoke attributes, working with the SyndicationFeed & Rss20FeedFormatter classes, and configuration-free WCF hosting in IIS.

You can also download the source code of the project built in the screencast.

Finally, if you're willing to do without video you can download just the audio as an MP3.

dotNetDevBuzz on Channel 9 Last Week

Sunday, March 08, 2009 8:28:19 PM (Pacific Standard Time, UTC-08:00)
Any of my technically savvy friends know that I'm a big fan of Channel 9. If you want the raw, inside view of Microsoft's developer world, it's a great place to start. That's why it was a big honor for me that my .NET Developer community website

   http://dotnet.ubbuzz.com

was featured on "This Week on Channel 9" last week.


  (see segment 7:45 - 10:25)

Thank you Dan Fernandez for covering our site! If you're unfamiliar with .NET Dev Buzz, then what are you waiting for? You'd better check it out!

Debugging the Future (Advanced .NET Debugging) Video Presentation

Tuesday, March 03, 2009 3:14:04 PM (Pacific Standard Time, UTC-08:00)
My esteemed colleague, friend, and fellow instructor at DevelopMentor Jason Whittington gave a great presentation on advanced .NET debugging recently at the Oklahoma City Developer's Group. They luckily recorded it on video and published it on their website so that it may "live on in the Google".

   Debugging The Future: The Video by Jason Whittington

 
kick it on DotNetKicks.com

Shout it

If you want to debug .NET applications right down to their memory footprint, this talk is for you.

If you like this type of presentation, be sure to check out the classes we offer at DevelopMentor.

Significant Advances in Unit Testing Windows Workflow

Sunday, January 18, 2009 11:57:03 AM (Pacific Standard Time, UTC-08:00)

This post describes a unit testing library for testing Windows Workflow Foundations. It is not a framework like HarnessIt, NUnit, or MsTest. Rather it's a library that can be used in conjunction with any of these testing frameworks.

Download the library with sample test project here: Kennedy.WorkflowTesting.zip (216 KB)

You can also just jump to the code.

If you like this post, be sure to kick it on DotNetKicks.com

First a Little History:

Last September I posted this teaser entitled Unit Testing Coming to a Workflow Near You. My intention was to post this article that you're reading now shortly thereafter when I got some free time to polish things up. In that previous post, I highlighted what I could determine to be the current state-of-the-art with regard to unit testing workflows, circa September 2008.

Then I heard through some inside sources that this MSDN Magazine article was about to come out:

     Foundations: Unit Testing Workflows and Activities by Matt Milner.

So I decided to see what Matt's article had to offer to the conversation. It's a good article to be sure. It covers a lot of the things I thought were undiscussed and yet important to unit testing WF (e.g. using WF services as points of dependency injection for mocks and stubs). Thanks Matt! I don't have to write about that now, but you'll see it used in the sample with my library.

What I was really waiting to see was would that article make this post redundant? After reading it, I can say that there's still a long way to go - and this library will get us most of the way there. Now let's get to the good stuff!

Significant Advances:

That's a pretty bold statement, significant advances: let's see if I can back it up. Here's what's missing in one way or another from all the previous work on unit testing WF. (Please note that this discussion is in no way intended to belittle the work of anyone quoted above, just to build on their work and advance testing for us all).

Problems with unit testing WF today that are solved by this library:

  1. Testing single activities: Testing single activities in isolation is hard.
  2. References to the activity: Direct access to the activity under test for asserting on its properties is nearly impossible.
  3. Waiting on workflows: Workflows run on background threads which means waiting for the outcome inside the test method is more cumbersome than necessary (ManualWorkflowScheduler is unnecessarily cumbersome as well).
  4. Untyped name/value collections as input: Using untyped name/value collections as input and return values is error prone (for testing and general use).
  5. Expected exceptions: Testing "failure as success" cases for error handling is essentially broken for WF: Either the exception type is lost, or the call stack is lost, or both.

The Sample:

Let me set the stage first before we see the test code. I have a somewhat realistic workflow which will exchange two stocks and either debit or credit your bank account with the difference. So you might want to sell 5 shares of Google and buy 10 shares of Microsoft and pocket the difference.

Here's the workflow which involves 4 different activities:

Testing On Single Activities:

The first thing to test is the individual activities (like BuyStock and DebitAccount). Here's the code to test selling a stock (some details omitted for simplicity, exact code follows later). This method uses my library class WfRunner for executing the test.

[TestClass]
public class WorkflowTests : IDisposable
{
    private WfRunner wfRunner = new WfRunner();

    [TestMethod]
    public void SellStockComputesCostCorrectlyTest()
    {
        StockDTO dto = new StockDTO( 7, "GOOG" );

        SellStockActivity sellActivity =
            wfRunner.RunSingleActivity<SellStockActivity>( dto );

        double price = testStockSvc.LookupPrice( dto.Ticker );
        int quantity = dto.Quantity;

        Assert.AreEqual( quantity * price, sellActivity.Cost );
    }

    // ...
}

This test method (SellStockComputesCostCorrectlyTest) is remarkable for several reasons:

  1. We are taking a single Activity, not a workflow, and executing it.

  2. We are passing a strongly typed DTO (data transfer object) rather than name/value pairs in a Dictionary<string, object>.

  3. Most Remarkably: We are getting the actual instance of the activity returned to us so that we can explore its properties. Notice that we assert on sellActivity.Cost directly:

    Assert.AreEqual( quantity * price, sellActivity.Cost );

    This is not easy to pull off - WF intentionally hides activities and workflows it creates behind workflow instances (proxies basically). This hack might be bad for production systems, but it *rocks* for unit testing.It means you get intellisense rather than programming against strings in name/value collections.

  4. We are not waiting for the workflow to complete or using the ManualWorkflowSchedule. WfRunner is doing that for us because the method RunSingleActivity is a blocking call.

Just so you don't think I'm trying to pull a fast one: Here's that same listing with all the gory details left in. Notice how we're using WF Services for DI with our test stubs.

[TestClass]
public class WorkflowTests : IDisposable
{
    //
    // Define some objects that will be used across all tests.
    //
    private IAccountService testAccountSvc;
    private IStockService testStockSvc;
    private Account testAccont;
    private WfRunner wfRunner = new WfRunner();

    public WorkflowTests()
    {
        //
        // Initialize common data for all tests. 
        // This is basically what the host of the wf-runtime would do
        // but we're using test doubles / stubs for our services.
        //
        Dictionary<string, double> stocks = new Dictionary<string, double>();
        stocks.Add( "goog", 350 );
        stocks.Add( "msft", 25 );

        testAccont = new Account( 1774, 5000 );

        this.testStockSvc = new TestStockService( stocks );
        this.testAccountSvc = new TestAccountLookup( testAccont );

        // Install these services for use by our WF activities.
        wfRunner.AddService( testStockSvc );
        wfRunner.AddService( testAccountSvc );
    }

    [TestMethod]
    public void SellStockComputesCostCorrectlyTest()
    {
        StockDTO dto = new StockDTO( 7, "GOOG" );

        SellStockActivity sellActivity =
            wfRunner.RunSingleActivity<SellStockActivity>( dto );

        double price = testStockSvc.LookupPrice( dto.Ticker );
        int quantity = dto.Quantity;

        Assert.AreEqual( quantity * price, sellActivity.Cost );
    }

    // ...

}

Expected Exceptions

That was pretty awesome huh? We solve several of our problems I identified above (testing single activities, references to the activity, waiting on workflows, and untyped name/value collections as input). The last one to cover is exceptions as success.

When we try to buy a stock and we don't have enough money, the workflow will throw an InsufficientFundsException. This type is a custom exception created as part of my wf application - it's not part of .NET. We want to test for this exception:

[TestMethod]
[ExpectedException(typeof (InsufficientFundsException))]
public void CannotBuyWithInsufficentFundsTest()
{
    ExchangeStocksDTO dto =         new ExchangeStocksDTO
            {
                AccountID = 1774,
                StockToBuy = "MSFT",
                StockToSell = "GOOG",
                SellQuantity = 5,
                BuyQuantity = 7000
            };

    wfRunner.RunWorkflow<ExchangeStocksWorkflow>( dto );
}

Notice that we're using the ExpectedException attribute. We just call RunWorkflow as a regular method and we get the exception back as a synchronous error. That is fantastic already. But look at the call stack:

SampleLibrary.InsufficientFundsException: Wrapped excpetion message: Insufficient funds for account 1774.
 ---> SampleLibrary.InsufficientFundsException: Insufficient funds for account 1774.
   at SampleLibrary.Account.Withdrawl(Double amount)
   at SampleLibrary.DebitAccount.Execute(ActivityExecutionContext ctx) 
   at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext)
   at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(Activity activity, ActivityExecutionContext executionContext)
   at System.Workflow.ComponentModel.ActivityExecutorOperation.Run(IWorkflowCoreRuntime workflowCoreRuntime)
   at System.Workflow.Runtime.Scheduler.Run()
   --- End of inner exception stack trace ---
   at Kennedy.WorkflowTesting.WfRunner.TransformAndThrowIfRequired(Exception realException)
   at Kennedy.WorkflowTesting.WfRunner.RunWorkflow[T](Dictionary`2 namedArgumentValues)
   at Kennedy.WorkflowTesting.WfRunner.RunWorkflow[T](Object workflowDTO)
   at SampleLibrary.Tests.WorkflowTests.CannotBuyWithInsufficentFundsTest()

The WfRunner class has determined there was an exception. Rather than just rethrowing it and losing the callstack (notice it's still intact), we do this operation called TransformAndThrowIfRequired. TransformAndThrowIfRequired takes the real exception, uses reflection to recreate another exception of that type and wraps the real exception as the inner exception.

This both preserves the exception type (critical for the ExpectedException behavior) and the callstack (critical for debugging).

Running these tests inside Visual Studio we get all green!

I hope you find this library adds significant value to unit testing of your Windows Workflows. Personally, I think it makes unit testing of your Windows Workflows practical in the real world.

Enjoy!
Michael

Note: This only applies to WF 3.0/3.5. WF 4.0 which is part of .NET 4 which is shipping the end of 2009 may change this considerably.

Test Driven Development, Approval Testing, and a Song - Oh Boy!

Wednesday, January 07, 2009 12:17:54 PM (Pacific Standard Time, UTC-08:00)

So my buddies Dan Gilkerson and Llewellyn Falco have been doing some brilliant, ground breaking work on advancing the state of unit testing and TDD with a concept they call Approval Testing.

To highlight the transition from

unit testing -> TDD -> BDD -> Approval Testing

Dan wrote a parody of the song Let It Be.

The Music Video: Let it BBD


If you like it, then kick it on DotNetKicks.com

MSDN Magainze: Web Apps That Support Long-Running Operations

Wednesday, December 24, 2008 9:18:04 AM (Pacific Standard Time, UTC-08:00)
I'm pleased to announce that MSDN Magazine just published my Windows Workflow article entitled

    "ASP.NET WORKFLOW: Web Apps That Support Long-Running Operations"

I hope you find it useful and interesting.


Just Launched: .NET Dev Buzz

Monday, October 13, 2008 6:04:39 PM (Pacific Standard Time, UTC-08:00)
I just launched the beta version of an entirely differnet kind of .NET community website:
.NET Dev Buzz
Bringing you absolutely fresh conversations about .NET and the Microsoft developer community.

http://dotnet.ubbuzz.com/
I'll follow this up with some highlights of a bunch of interesting .NET concepts being used here.

For starters, consider this. This is a website built in ASP.NET web forms, but there is no view state, no form posts, and no (visible) aspx files. It's fully RESTful.



If you like it, then kick it on DotNetKicks.com

Unit Testing Coming to a Workflow Near You

Tuesday, September 30, 2008 7:50:29 AM (Pacific Standard Time, UTC-08:00)
[Update: See the follow up post "Significant Advances in Unit Testing Windows Workflow"]

If you've been working with Windows Workflow, you'll find it has some cool features for orchestration, long running operations, state machines, etc.

However you won't find very much support for Test Driven Development (TDD) or unit testing in general. In fact the architecture that makes Windows Workflow powerful (strict separation of workflow, activities, and the host for example) really gets in the way of unit tests.

There has been some work done on unit testing Windows Workflows. Here's some links:

These are all very creative solutions. But, personally I find all of them more complex than they need to be. So in the near future I'll be putting together some libraries and samples on unit testing Windows Workflow. I think you'll find them far more powerful and at the same time simpler than anything out there.

So until I get that finished, if you have any feedback or considerations on unit testing Windows Workflow I'd love to hear it. If there are other articles I'm missing, please post them in the comments.

I think you're going to like this…



kick it on DotNetKicks.com