Welcome to MSDN Blogs Sign in | Join | Help

About a month ago, Ed Pinto and I did an interview with the .NET Rocks guys on What is new in WCF for .NET 4 and it just went live today.  Ed covered most of the detailed technical stuff, I filled in more along the value proposition side of things, and Carl and Richard added their usual comic relief and excellent interviewing skills.  It was fun.  Hope you enjoy!

This is part eight of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Using the new WCF WebHttp Services API for content negotiation
  • Returning content with custom formats in addition to XML and JSON
  • Configuring WCF WebHttp Services with the WebHttpEndpoint

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

 

Step 1: Creating the DirectoryService class

If you have downloaded the TeamTask service code for any of the first seven parts of this blog post series, you may have noticed that all of the valid URIs for the service begin with either “http://<baseAddress>/TeamTask/Tasks” or “http://<baseAddress>/TeamTask/Users”.  This makes sense because the service is really made up of a collection of task resources and a collection of user resources; we even refactored the service to better align with this resource model in part six.

However, if you try to navigate to just “http://<baseAddress>/TeamTask” (without specifying either “Users” or “Tasks”) you’ll find that you won’t get a response from the service.  Obviously, this is awkward; it’s akin to going to a book seller’s website where there is a page for each book, but not a main page from which to find the individual book pages.

In this part of the blog post series, we’ll rectify this issue by adding a new service class that will provide a directory of all of the resource collections exposed by the service.   At this point the directory will only list the task and user resource collections, but its easy to imagine adding new functionality to the TeamTask service in the future that might result in new resource collections appearing in the directory.

To demonstrate the advanced formatting APIs introduced in WCF WebHttp Services for .NET 4, we’ll implement this new DirectoryService class so that it can return responses in XML, JSON, or Atom.  In the next part of this blog post series, we’ll extend the DirectoryService to also provide responses in a help-page-like HTML format.

We’ll start by creating the DirectoryService class from a copy of the TaskService.cs source file.

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. In the Solution Explorer window select the newly renamed TaskService.cs file and press Ctrl+C, Ctrl+V to create a copy of the TaskService.cs file in the TeamTask.Service project.  The new file should be named “Copy of TaskService.cs”.

  3. Rename the “Copy of TaskService.cs” file to “DirectoryService.cs” by right-clicking on it in the Solution Explorer window and selecting “Rename” from the context menu.

  4. Open the DirectoryService.cs file in the code editor.  Because this file is actually a copy of the TaskService.cs file, it will contain a second definition of the TaskService class.  Rename the class to “DirectoryService”, making sure NOT to update references throughout the project.

  5. Delete all of the methods of the newly renamed DirectoryService class.

  6. Add a single operation, GetDirectory(), which will have an empty string UriTemplate value and a return type of Message as shown below.  You’ll also have to add “using System.ServiceModel.Channels;”.

        namespace TeamTask.Service
        {
            [ServiceContract]
            [AspNetCompatibilityRequirements(RequirementsMode = 
                AspNetCompatibilityRequirementsMode.Allowed)]
            [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
            public class DirectoryService
           

            
                [WebGet(UriTemplate = "")]
                public Message GetDirectory()
                {

                } 
            }
        }

Helpful Tip: Using a return type of Message with the GetDirectory() operation will allow us to return any arbitrary content in the body of our response.  As we’ll see below, WCF WebHttp Services for .NET 4 introduces a useful set of Create*Response() APIs to make creating the Message instance painless.  For binary data there is the CreateStreamResponse() method.  For text-based custom formats there is CreateTextResponse().  There are also similar methods for XML, JSON and Atom, making it possible to return any arbitrary content or regular XML/JSON from the same operation.

Of course, if you only need to generate responses in XML and/or JSON then there is no need to use the Message return type.  You can return a strongly-typed class for your content and use the automatic and explicit format selection features in WCF WebHttp Services for .NET 4 as we demonstrated in part four of this blog post series.

 

Step 2: Adding the DirectoryService Route

Just as we did in part six of this blog post series when we created the TaskService and UserService classes, we need to add a route in the Global.asax file for the new DirectoryService class. 

When we added the TaskService and UserService routes in part six we used string literals for the route paths.  For example, the TaskService route in the Global.asax file is currently given as:

    RouteTable.Routes.Add(new ServiceRoute("Tasks", factory, typeof(TaskService)));

where “Tasks” is the route path.  There is nothing inherently wrong with using string literals except that we can’t access the route paths from our code, which we’re going to need to do when we implement the DirectoryService class.  Therefore as we add the route for the DirectoryService class, we’ll also replace the string literal route paths with public constants.

  1. Open the TaskService.cs file in the code editor and add the following constant to the TaskService class:

        public const string Route = "Tasks";

  2. Open the UserService.cs file in the code editor and add the following constant to the UserService class:

        public const string Route = "Users";

  3. Open the DirectoryService.cs file in the code editor and add the following constant to the UserService class:

        public const string Route = "";

  4. Open the Global.asax file in the code editor and replace the route paths for the TaskService route and the UserService route with the Route constants from their respective classes like so:

        RouteTable.Routes.Add(new ServiceRoute(TaskService.Route, factory,
            typeof(TaskService)));
        RouteTable.Routes.Add(new ServiceRoute(UserService.Route, factory,
            typeof(UserService)));

  5. Add an additional route for the DirectoryService like so:

        RouteTable.Routes.Add(new ServiceRoute(DirectoryService.Route, factory,
            typeof(DirectoryService)));

Step 3: Creating the ResourceCollection Type

We’re almost ready to implement the DirectoryService class.  However, we still need to create a type that will hold all of the information about a given resource collection just like the Task and User types hold all of the information about a given task or user.  We’ll create a ResourceCollection class to fulfill this role.

  1. In the Solution Explorer (Ctrl+W, S) right click on the TeamTask.Service project and select “Add”—>”Class” from the context menu that appears.  In the Add New Item window enter the name “ResourceCollection.cs” and hit enter.

  2. Open the newly created ResourceCollection.cs file in the code editor and add the code as shown below:

        public class ResourceCollection
        {
            internal string Route { get; set; }
            public string Name { get; set; }            
            public string Description { get; set; }

            public Uri Uri
            {
                get
               
    {
                    return new Uri(this.Route, UriKind.Relative);
                }
                set { }
            }

            public Uri HelpUri
            {
                get
               
    {
                    return new Uri(this.Route + "/help", UriKind.Relative);
                }
                set { }
            }
        }

    Notice that the Route property is specified as internal.  The access modifiers on the properties matter because instances of the ResourceCollection class will be serialized in the responses from the GetDirectory() operation.  The route path is needed to create the URI’s but routes themselves are an internal implementation detail that we don’t want to expose.  By making the Route property internal we ensure that it won't be included when the ResourceCollection class is serialized.

Step 4: Implementing the DirectoryService Class

We are now ready to implement the DirectoryService class.  We’ll be able to see how the new content negotiation and advanced formatting APIs in WCF WebHttp Services for .NET 4 will allow us to return responses in XML, JSON or Atom depending on the client’s preference.

The TeamTask service only exposes two collections of resources (tasks and users) and the information about these resource collections doesn’t change after the service starts.  So we’ll first add to the DirectoryService class a static read-only list that contains a ResourceCollection instance for the TaskService and another for the UserService.  Second, we’ll implement the GetDirectory() operation to use this static list of ResourceCollection instances when generating responses. 

Since the GetDirectory() operation will support XML, JSON and Atom, we’ll need to be able to determine the client’s preference in order to generate the response in the correct format.  We could use a “format” query string parameter as we did in part four of this blog post series with the GetTasks() operation.  However, we’ll use the HTTP Accept header of the incoming request to determine the response format in order to demonstrate the content negotiation features in WCF WebHttp Services for .NET 4.

Parsing an HTTP Accept header to determine a client’s content-type preference can be a little tricky since the HTTP specification allows for media ranges with wildcard characters and relative quality factors.  Fortunately, with WCF WebHttp Services in .NET 4 a new GetAcceptHeaderElements() method is available via the WebOperationContext and it handles all of this Accept header parsing, returning a list of strongly-typed ContentType instances in the order of client preference.

Knowing the content-type that the client prefers, we’ll use either the CreateXmlResponse(), CreateJsonResponse() or CreateAtom10Response() methods that hang off the WebOperationContext to serialize the list of ResourceCollection instances into the body of a Message instance in the appropriate format.

  1. Open the DirectoryService.cs file in the code editor.

  2. Add the following list of ResourceCollection instances as a static member of the DirectoryService class:

        private static readonly List<ResourceCollection> resourceCollections = 
            new List<ResourceCollection>()
            {
                new ResourceCollection() 
                {
                    Route = TaskService.Route,
                    Name = "Tasks Collection",
                    Description = "A collection of the current tasks for the team.",  
                },
                new ResourceCollection()
                {
                    Route = UserService.Route, 
                    Name = "Users Collection",
                    Description = "A the collection of the current users on the team."
                } 
            };

  3. Implement the GetDirectory() operation as shown below.  You’ll also need to add “using System.Net.Mime;” and “using System.ServiceModel.Syndication;” to the source file.

        [WebGet(UriTemplate = "")]
        public Message GetDirectory()
        {
            WebOperationContext context = WebOperationContext.Current;
            IncomingWebRequestContext request = context.IncomingRequest;

            foreach (ContentType acceptElement in
                request.GetAcceptHeaderElements())
            {
                switch (acceptElement.MediaType.ToUpperInvariant())
                {
                    case "APPLICATION/XML":
                    case "TEXT/XML":
                        return context.CreateXmlResponse(resourceCollections);
                    case "APPLICATION/JSON":
                        return context.CreateJsonResponse(resourceCollections);
                    case "APPLICATION/ATOM+XML":
                        SyndicationFeed feed = GetFeed();
                        return context.CreateAtom10Response(feed);
                }
            }

            return context.CreateXmlResponse(resourceCollections);
        }

    Notice how the XML and JSON responses are being created.  The static read-only list of ResourceCollections is being passed into the CreateXmlResponse() and CreateJsonResponse() methods.  Both of these methods will create a new Message instance and serialize the list of ResourceCollections into the body of the Message in their respective format.

    Creating the Atom response requires a little more code, but only because we have to map the ResourceCollection type into the Atom format.  While the CreateXmlResponse() and CreateJsonResponse() methods will take any type that can be successfully serialized, the CreateAtom10Response() method expects either a SyndicationFeed instance, a SyndicationItem instance, or a ServiceDocument instance.  Here we are supplying a SyndicationFeed that we create from the list of ResourceCollections in the GetFeed() method.  We’ll implement the GetFeed() method next.

  4. Copy the following implementation of the GetFeed() method into the DirectoryService class:

        private static SyndicationFeed GetFeed()
        {
            DateTime itemDate = DateTime.Now;
            string feedTitle = "Team Task Resources";
            string feedDescription = "The Resource Collections of the Team Task Service.";
            Uri feedUri = new Uri(DirectoryService.Route, UriKind.Relative);

            var items = resourceCollections.Select(resource =>
                new SyndicationItem(resource.Name, resource.Description,
                    resource.Uri, "tag:???", itemDate));

            return new SyndicationFeed(feedTitle, feedDescription, feedUri, items);
        }

    The GetFeed() method creates a SyndicationFeed by projecting the task and user ResourceCollection instances into SyndicationItems using the LINQ Select() method.  Along with the syndication items, the feed’s title, description and URI are also provided to the SyndicationFeed constructor.

Helpful Tip: The SyndicationFeed, SyndicationItem and ServiceDocument classes are all a part of the System.ServiceModel.Syndication namespace, which provides the functionality for working with Atom, RSS and other syndication formats in WCF.

 

Step 5: Getting the Resource Collections on the Client

With our DirectoryService implemented, we’re ready to retrieve the list of resource collections with our console client application.  In the client app we’ll send three requests to http://localhost:8080/TeamTask/, each with different accept header values, and we’ll write out the responses to the console to verify that we are getting the correct content-type.

  1. Open the Program.cs file from the TeamTask.Client project in the code editor.

  2. Add the following static method to the  Program class:

        static void GetResourceCollections(HttpClient client, string acceptType)
        {
            Console.WriteLine("Getting the resource collections as '{0}':", acceptType);
            using(HttpRequestMessage request =
                     new HttpRequestMessage("GET", string.Empty))
            {
                request.Headers.Accept.AddString(acceptType);
                using(HttpResponseMessage response = client.Send(request))
                {
                    response.EnsureStatusIsSuccessful();
                    if(response.Content.ContentType.Contains("xml"))
                    {
                        Console.WriteLine(response.Content
                            .ReadAsXElement().ToString());
                    }
                    else
                   
    {
                        Console.WriteLine(response.Content.ReadAsString());
                    }
                }
            }
            Console.WriteLine();
        }

    This client code is very similar to the client code we’ve been writing since part two of this blog post series.  The only noteworthy point is that we’re checking if the response content-type is some form of XML and if so we’re using the ReadAsXElement() extension method for the HttpContent class to get whitespace formatting when writing out to the console.

  3. Now we’ll implement the Main() method to retrieve the resource collections three separate times with different HTTP Accept header values.  Replace any code within the Main() method with the following code:

        using (HttpClient client = new HttpClient("http://localhost:8080/TeamTask/"))
        {
            GetResourceCollections(client, "application/xml");
            GetResourceCollections(client, "application/atom+xml");
            GetResourceCollections(client, "application/json");
            Console.ReadKey();
        }

  4. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    ResourceCollectionsInConsole 

Step 6: Disabling the Help page for the DirectoryService

As we demonstrated in the previous step, our DirectoryService behaves as expected and returns either XML, JSON or Atom depending on the client’s preference.  The TeamTask service now feels like a single unified service and not just two disparate collections of Task and User resources.

However, there is one awkward aspect of the DirectoryService that we need to rectify—the presence of the automatic help page.  If you were to start the TeamTask service and navigate to http://localhost:8080/TeamTask/help you’d find a not-too-useful help page.  A useful help page would provide the list of resource collections for the TeamTask service and links to the respective User and Task collection help pages.  We’ll create such a help page in the next part of this blog post series.  However, the automatic help page generated for the DirectoryService doesn’t provide any of this information; it only lists the GetDirectory() operation.  Therefore, we’ll disable the automatic help page for the DirectoryService.

  1. Open the Web.config file from the TeamTask.Service project in the XML editor.

  2. The configuration for WCF is under the <system.serviceModel> element and should currently look like so:

        <system.serviceModel>
          <
    serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
          <
    standardEndpoints>
            <
    webHttpEndpoint>
             
    <standardEndpoint name="" helpEnabled="true"  
                         automaticFormatSelectionEnabled
    ="true"/>
            </
    webHttpEndpoint>
          </
    standardEndpoints>
        </
    system.serviceModel>

    Notice that there is a single <standardEndpoint> element that has an empty-string name and the help page and automatic format selection features enabled.

    Now you might think that disabling the automatic help page for the DirectoryService is a simple matter of setting the automaticFormatSelectionEnabled value to “false”.  However, this won’t work because the standard endpoint declared in the config snippet above is the default WebHttpEndpoint (the empty-string name makes it the default) and it applies to all of the services that make up the TeamTask service (TaskService, UserService, and now DirectoryService).  If we set the automaticFormatSelectionEnabled value to “false”, we’ll also be disabling the help page for the TaskService and UserService, which we don’t want to do.  Instead we need to add a second named webHttpEndpoint and disable the help page on this named webHttpEndpoint.

  3. Add a second <standardEndpoint> element under the <webHttpEndpoint> element that has a name of “DirectoryEndpoint” like so:

        <webHttpEndpoint>
          <standardEndpoint name="" helpEnabled="true"  
                     automaticFormatSelectionEnabled
    ="true"/>
          <
    standardEndpoint name="DirectoryEndpoint"/>
        </
    webHttpEndpoint>

    Because the help page is disabled by default this DirectoryEndpoint will not expose an automatic help page.

  4. Now that we have a named webHttpEndpoint, we need to indicate to the WCF infrastructure that the DirectoryService should use the DirectoryEndpoint instead of the default nameless webHttpEndpoint.  To do this we need to explicitly declare the DirectoryService service endpoint in the Web.config by adding the following XML snippet under the <system.ServiceModel> element:

        <services>
          <
    service name="TeamTask.Service.DirectoryService">
            <
    endpoint contract="TeamTask.Service.DirectoryService"
                kind="webHttpEndpoint"
                endpointConfiguration="DirectoryEndpoint" />
          </
    service>
        </
    services>

    The <service> element has a name attribute in which we’ve supplied the namespace- qualified class name of the DirectoryService and an <endpoint> child element.  On the <endpoint> element we specify that the endpoint kind is “webHttpEndpoint” and that the endpointConfiguration is “DirectoryEndpoint”, the named webHttpEndpoint that we just added under the <standardEndpoints> element. We also have to specify the contract, which happens to be the namespace-qualified class name of the DirectoryService although it would be a different type if we had separated the contract from the actual implementation for the DirectoryService.

    Note: We don’t need service endpoints for the TaskService or the UserService, since they use the default nameless webHttpEndpoint configuration.

  5. To verify that these configuration changes disable the help page for just the DirectoryService and not the TaskService or UserService, start without debugging (Ctrl+F5) and navigate to http://localhost:8080/TeamTask/help.  In Internet Explorer, the absence of the help page will result in an html page like the following:

    DirectoryServiceHelpPageDisabledInBrowser

    Now navigate to http://localhost:8080/TeamTask/Tasks/help or to http://localhost:8080/TeamTask/Users/help and you should still see the help pages of the tasks or users resource collections respectively.

Helpful Tip: Standard endpoints are one of the service configuration improvements introduced in WCF in .NET 4.  Standard endpoints are re-usable predefined endpoints that make service configuration more straightforward and WCF WebHttp Services in .NET 4 offers two of them: WebHttpEndpoint and WebScriptEndpoint.  For the TeamTask service we’ve been using the webHttpEndpoint; it is the standard endpoint you’ll generally want to use for web services that return XML, JSON and other web formats.  The webScriptEndpoint is for services that are designed specifically to support ASP.NET AJAX browser clients.

 

Helpful Tip: If you’ve used WCF 3.5 or the WCF REST Starter Kit to build non-SOAP HTTP services in the past, you may be aware that WCF WebHttp Services are simply WCF services that use the WebHttpBinding and the WebHttpBehavior.  The WebHttpEndpoint is really just a predefined standard endpoint that uses both of these.  Therefore in WCF WebHttp Services for .NET 4 you no longer need to configure the WebHttpBinding or the WebHttpBehavior because the new WebHttpEndpoint surfaces all of the useful settings of both of them in one place in the Web.config file.

 

Next Steps: Creating Views with T4

We’ve accomplished a lot in this part of the blog post series.  We’ve implemented the new DirectoryService to provide clients with a list of the resource collections exposed by the TeamTask service.  We demonstrated how to use the new content negotiation API to easily determine the client’s preferred response format and how to use the new Create*Response() APIs to generate the responses in that given format.  Lastly, we saw how to configure the DirectoryService differently from the other services that make up the TeamTask service by creating a named webHttpEndpoint and referencing it from a new service endpoint for the DirectoryService.

In part nine of this blog post series we’ll continue to explore the new features introduced with WCF WebHttp Services in .NET 4 that make it possible to return arbitrary content in the body of an HTTP response.  In particular, we’ll demonstrate how to generate responses in a WCF WebHttp Service using T4 to create templates that behave exactly like views in a model-view-controller architecture.

Randall Tombaugh
Developer, WCF WebHttp Services

This is part seven of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Creating ASP.NET cache profiles in configuration for use with WCF WebHttp Services
  • Using the [AspNetCacheProfile] attribute to associate service operations with cache profiles

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

 

Step 1: Specifying that Responses should be Cached

Caching is fundamental to HTTP, so its not surprising that since .NET 1.1 there has been a rich mechanism for declaratively setting the caching policy of a webpage in ASP.NET.  Known as Output Caching, it allows developers to specify many aspects of how a page should be cached.  Should the page be cached on the server to be used again for future requests, or should an HTTP Cache-Control header be included with the response for client (or proxy) caching?  When should the cached content expire and become considered stale?  Are there certain query string parameters in the URI or HTTP headers in the request that will require different responses and therefore will need to be cached separately?  ASP.NET output caching can accommodate all of these complexities.

With .NET 4, the rich caching capabilities of the ASP.NET output cache can now be leveraged from within a WCF WebHttp Service when ASP.NET compatibility mode is enabled.  Specifying that a response from a service operation should be cached in some form is done by adding an [AspNetCacheProfile] attribute to the operation.  The [AspNetCacheProfile] attribute will then point to an ASP.NET cache profile in the Web.config where the details of how the response should be cached are specified.

With the TeamTask service we’ll begin caching the user data returned by the GetUsers() and GetUser() operations.  We’ll assume that this user data changes very rarely, making it appropriate for caching.  In this step we’ll add the [AspNetCacheProfile] attribute to the service operations and in the next step we will configure their respective cache profiles in the Web.config file.

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. Open the UserService.cs file from the TeamTask.Service project in the code editor.

  3. On the GetUsers() operation, add an [AspNetCacheProfile] attribute with the cache profile name “UsersCollection” like so:

        [AspNetCacheProfile("UsersCollection")]
        [Description("Returns the users on the team.")]
        [WebGet(UriTemplate = "?skip={skip}&top={top}&manager={userName}")]
        public List<User> GetUsers(int skip, int top, string userName)

  4. On the GetUser() operation, add an [AspNetCacheProfile] attribute with the cache profile name “SingleUser” like so:

        [AspNetCacheProfile("SingleUser")]
        [Description("Returns the details of a user on the team.")]
        [WebGet(UriTemplate = "{userName}")]
        public User GetUser(string userName)

Step 2: Configuring Server-side Caching

Let’s first configure the “UsersCollection” cache profile for the GetUsers() operation.  We’ll configure the profile to cache the responses on the server. 

In addition to the caching location, we’ll need to specify some other details in the cache profile.  For the sake of demonstrating the feature we’ll specify that the cached responses should expire after one minute, but we could set them to expire after an hour, a day or even longer.  Since the GetUsers() operation accepts the “skip”, “top” and “manager” query string parameters and these change the content of the response, we’ll need to specify this in the cache profile.  Lastly, with automatic format selection enabled, the format (XML or JSON) of the response will vary depending on the value of the request’s HTTP Accept Header, so we’ll also need to account for this in the cache profile configuration.

  1. Open the Web.Config file of the TeamTask.Service project in the code editor.  Since cache profiles are an ASP.NET feature, they are specified under the <system.Web> element, which should currently have the following configuration elements specified:

        <system.web>
          <
    compilation debug="true" targetFramework="4.0" />
        </
    system.web>

  2. All of the caching related configuration is specified in a <caching> element under the <system.web> element.  Add the caching related configuration elements as follows:

        <system.web>
          <
    compilation debug="true" targetFramework="4.0" />
        
          <
    caching>
            <
    outputCache enableOutputCache="true"/>
            <
    outputCacheSettings>
              <
    outputCacheProfiles>
              </
    outputCacheProfiles>
            </
    outputCacheSettings>
          </
    caching>
        </
    system.web>

    ASP.NET output caching has to be enabled for any of the cache profiles to be applied and this is done with the “enableOutputCache” attribute on the <outputCache> element.  The cache profiles themselves are specified under the <outputCacheProfiles> element.

  3. Add the following cache profile under the <outputCacheProfiles> element:

        <outputCacheProfiles>
          <
    add name="UsersCollection" location="Server" duration="60"
                  varyByParam="skip; top; manager" varyByHeader="Accept"/>
        </
    outputCacheProfiles>

    The settings on the cache profile should be fairly self-explanatory.  The duration is specified in seconds, so a value of “60” indicates that the response should be cached for a single minute.  The varyByParam attribute specifies that requests with different values for the “skip”, “top” and “manager” URI query string parameters should be considered different requests such that the respective responses are cached separately.  Likewise, the varyByHeader attribute specifies that requests with different Accept header values should be considered different requests.  Note: For the varyByParam and varyByHeader attributes, use a semi-colon separated list if there is more than a single value.

Be Careful:Requests that require authorization should not have their responses cached, because the authorization is not performed when the response is served from the cache.  Caching such responses would introduce a serious security vulnerability.  Usually, requests that require authorization provide user-specific data and therefore server-side caching is not even beneficial.  In such situations, client-side caching or simply not caching at all will be more appropriate.

 

Step 3: Configuring Client-side Caching

One of the benefits of using ASP.NET output caching is that it provides a single programming model for both server-side and client-side caching even though the two mechanisms are very different.  Server-side caching is invisible to the client.  After a response has been cached on the server, future responses are served from the cache such that the request never even reaches the WCF WebHttp Service code. Client-side caching on the other hand is achieved by adding an HTTP Cache-Control header to the response.  Future request from (supposedly different) clients would still be handled by the WCF WebHttp Service code.

Configuring server-side verse client-side caching is a simple matter of setting the location attribute on the cache profile.  For the “UsersCollection” cache profile in step two, we set the location to “Server”.  For the “SingleUser” cache profile, we’ll set the location to “Client” to enable client-side caching.  Of course, it is also possible to use server-side and client-side caching in tandem—see here for the possible values of the location attribute.

  1. Open the Web.Config file from the TeamTask.Service project in the code editor.

  2. Add the “SingleUser” cache profile under the <outputCacheProfiles> element as shown below:

        <outputCacheProfiles
          <
    add name="UsersCollection" location="Server" duration="60"
                  varyByParam="skip, top, manager" varyByHeader="Accept"/> 
          <
    add name="SingleUser" location="Client" duration="60
                  varyByParam="none"/> 
        </
    outputCacheProfiles>

    Notice that the location of the "SingleUser" cache profile is "Client".  Note: Because we are configuring client-side caching, the query string parameters and HTTP headers of the request aren't relevant.  However, a varyByParam value is required, so we’ve set the value to “none”.

Helpful Tip: It is possible to configure an SQL dependency with cached responses in WCF WebHttp Services.  Once configured, the ASP.NET cache will poll a given table of your database such that any changes to the table will result in the cached response being evicted.  For more information on configuring SQL dependencies with the output cache, see here

 

Step 4: Verifying that Responses are Cached

Now that we have our cache profiles configured we’re ready to demonstrate that the responses are indeed cached.  To verify the client-side caching we’ll write some client code to print the Cache-Control header from the response to the console.  To verify the server-side caching we’ll set a break-point in the service code and send two HTTP GET requests, noting that the debugger breaks with the first request but not the second (since it is served from the cache).

  1. Open the Program.cs file from the TeamTask.Client project in the code editor.

  2. In the static GetUser() method, add a line of code to write the Cache-Control header value from the response to the console like so:

        using (HttpResponseMessage response = client.Send(request))
        {
            Console.WriteLine("  Cache-Control: {0}",
                response.Headers.CacheControl);
            Console.WriteLine("  Status Code: {0}", response.StatusCode);
            Console.WriteLine("  Content: {0}", response.Content.ReadAsString());
        }

  3. Now we’ll implement the Main() method to simply call GetUser() once and GetUsers() three times.  Replace any code within the Main() method with the following code:

        using (HttpClient client = new HttpClient("http://localhost:8080/TeamTask/"))
        {
            GetUser(client, "user1", "application/json"); 
             
            Console.WriteLine(
                "Press Enter to call GetUsers() with top=3.  Should hit the breakpoint...");
            Console.ReadKey();
            client.Get("Users/?top=3");

            Console.WriteLine(
                "Press Enter to call GetUsers() with top=3 again. Should hit in the cache...");
            Console.ReadKey();
            client.Get("Users/?top=3");

            Console.WriteLine(
                "Press Enter to call GetUsers() with top=2. Should hit the breakpoint...");
            Console.ReadKey();
            client.Get("Users/?top=2");

            Console.ReadKey();
        }

  4. Before we start the server and client, we need to set a breakpoint in the GetUsers() operation.  Open the UserService.cs file from the TeamTask.Service project in the code editor and click on the breakpoint bar along the left-hand side to add a breakpoint like so:

    BreakpointInVisualStudio

  5. Start with debugging (F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window (Ctrl+W, S) and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    CacheControlHeaderInConsoleNotice that the Cache-Control header is present with a max-age of 60, which agrees with the duration value we used in the “SingleUser” cache profile.

  6. With the client console application in focus, press "Enter" as directed so that the client application continues to execute. 

    You should find that the service breaks after the first request to the GetUsers() operation.  You can press (F5) to continue executing the service.  The service should not break after the second request because the response is being served from the cache and the GetUsers() operation never executes.  However, the service should break on the third request because it has a different value for the “top” query string parameter and the response can’t be served from the cache.

Next Steps: Returning Custom Formats from WCF WebHttp Services

Back in part four of this blog post series we demonstrated the first-class support for XML and JSON in WCF WebHttp Services for .NET 4.  While XML and JSON are certainly popular web content-types, there are others.  In part eight of this blog post series we’ll reinvestigate the format support included with WCF WebHttp Services in .NET 4 and demonstrate how to send responses with other content-types like Atom feeds, plain text, or even binary data.

Randall Tombaugh
Developer, WCF WebHttp Services

This is part six of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Composing multiple WCF WebHttp Services into a single web service
  • Using ASP.NET routes with WCF WebHttp Services

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

Step 1: Refactoring the TeamTask Service into Resources

So far the TeamTask service that we’ve been implementing over the past five blog posts has had a manageable number of operations.  The service exposes an operation for retrieving a list of tasks, another for updating a single user and a third for updating a single task.  All of these operations are implemented within a single class—the TeamTaskService class. 

There is nothing necessarily wrong with this single service class design.  However, as we add more operations, our TeamTaskService class will continue to grow and we’ll end up with one large monolithic class.  As has been shown time and time again, monolithic entities tend to be difficult to maintain and they make code reuse problematic.  If we intend to continue adding functionality to the TeamTask service (and we do), we would be well served to start thinking of our service as a collection of resources and refactor our code to reflect this new mentality.  

A resource is technically anything with an addressable state.  In HTTP, a given resource is addressable because it has an associated URI and its current state is represented in the response from an HTTP GET request. 

However, using a more informal mental model of a resource as just a “thing” can be helpful.  It’s pretty easy to divide our TeamTask service into “things”.  Our “things” happen to be tasks and users.  We’ll start by refactoring our single TeamTaskService class into two separate service classes that will better represent our resources: a TaskService and a UserService.

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. Rename the TeamTaskService.cs file to “TaskService.cs” by right-clicking on it in the “Solution Explorer” window (Ctrl + W, S) and selecting “Rename” from the context menu.  A dialog box should appear asking if you want to rename all references in the project to the code element “TeamTaskService”.  Select “Yes”.

  3. In the “Solution Explorer” window select the newly renamed TaskService.cs file and press Ctrl+C, Ctrl+V to create a copy of the TaskService.cs file in the TeamTask.Service project.  The new file should be named “Copy of TaskService.cs”.

  4. Rename the “Copy of TaskService.cs” file to “UserService.cs” by right-clicking on it in the “Solution Explorer” window and selecting “Rename” from the context menu.

  5. Open the UserService.cs file in the code editor.  Because this file is actually a copy of the TaskService.cs file, it will contain a second definition of the TaskService class.  Rename the class to “UserService”, making sure NOT to update references throughout the project.

  6. In the UserService.cs file, delete the GetTasks() and UpdateTask() operations since they apply to tasks.

  7. Likewise, in the TaskService.cs file, delete the GetUser() operation as it applies to users and not tasks.      

Helpful Tip: The concept of a resource is central to the set of architectural constraints that make up REST.  We won’t be discussing the theory behind REST but there are plenty of articles, wiki entries, and books that discuss REST in depth.  That said, the remainder of the posts in this blog series will demonstrate how to build web services that are more RESTful in nature with WCF WebHttp Services in .NET 4.

 

Step 2: Adding Routes for our Task and User Services

We now have two separate service classes: a TaskService class that contains all of the task related operations and a UserService class that contains all of the user related operations.  Having two separate service classes means that we now need a mechanism to indicate which service class should handle a given incoming request.  Routes happen to be this mechanism.

Routes provide a mapping from a base address path to a service class. Originally an ASP.NET feature, routes can be used with WCF WebHttp Services when ASP.NET compatibility mode is enabled.  We’ve actually been using routes all along.  We just weren’t using the feature in an interesting way since we only had a single route that mapped an empty string path to the now-defunct TeamTaskService class.

For the TaskService class, we will register the route path “Tasks”.  This means that only requests with a URI base address of the form “http://<baseAddress>/Tasks” will be handled by the TaskService.  Likewise, we will map the route path “Users” to the UserService class.

A benefit of using routes is that we can now simplify the UriTemplates associated with our service operations.  If you look at the UriTemplates used with the TaskService operations, you’ll notice that they both begin with a “Tasks” path segment.  Likewise, the single operation on the UserService class begins with a “Users” path segment.  We’ll cut these first path segments from our UriTemplates since our routes will now have this “routing” information. 

Before the refactoring we had really collapsed the “routing” information in our URIs into the “operation” information of the UriTemplates.  With the refactoring, we rectify this issue.  The diagram below shows the improved scheme for mapping from request URIs to service operations:

UriRoutingFigure     

Routes are registered in the Global.asax file.  If you’ve worked with ASP.NET before, you may be familiar with the Global.asax file.  It provides the capability to respond to application-level events.  In this case, we want to respond to the application start event, which will occur when IIS receives the very first request to the TeamTask service.  It is in the handler for the application start event that we will register our routes for the TaskService and UserService classes.

  1. Open the Global.asax file in the code editor.  You should find that the RegisterRoutes() method has a single route registered like so:

        private void RegisterRoutes()
        {
            RouteTable.Routes.Add(new ServiceRoute("",
                new WebServiceHostFactory(), typeof(TaskService)));
        }

    Note: If the service type being registered in your code is of type TeamTaskService, then the references to TeamTaskService were not correctly updated when the class was renamed to “TaskService” in step one.

  2. Replace the the RegisterRoutes() method with the following implementation:

        private void RegisterRoutes()
        {
            WebServiceHostFactory factory = new WebServiceHostFactory();
            RouteTable.Routes.Add(new ServiceRoute("Tasks", factory,
                typeof(TaskService)));
            RouteTable.Routes.Add(new ServiceRoute("Users", factory,
                typeof(UserService)));
        }

    This new implementation adds two ServiceRoute instances to the Routes collection of the static RouteTable.  The first ServiceRoute instance provides a mapping from the route path “Tasks” to the TaskService class and the second instance provides a mapping from the route path “Users” to the UserService class.

  3. Open the TaskService.cs file in the code editor and remove the "Task" path segments from the UriTemplate values on the GetTasks() and UpdateTask() operations like so:

        [WebGet(UriTemplate =
            "?skip={skip}&top={top}&owner={userName}&format={format}")]
        public List<Task> GetTasks(int skip, int top, string userName, string format)

    and,

        [Description("Allows the details of a single task to be updated.")]
        [WebInvoke(UriTemplate = "{id}", Method = "PUT")]
        public Task UpdateTask(string id, Task task)

  4. Open the UserService.cs file in the code editor and remove the "User" path segments from the UriTemplate values on the GetUser() operation like so:

        [WebGet(UriTemplate = "{userName}")]
        public User GetUser(string userName)

Helpful Tip: Routes in ASP.NET can actually include variables much like UriTemplates.  However, be aware that in WCF WebHttp Services in .NET 4 only literal route paths are supported, as UriTemplates already provide URI variable support.

 

Step 3: Adding the GetTask() Operation

With the refactoring complete, let’s add some new operations to round out the functionality provided by the TeamTask service.  We already have the GetUser() operation for retrieving the details of a single user, but we don’t have a similar operation for retrieving the details of a single task.  So let’s add a GetTask() operation that will perform this function.

The GetTask() operation will use some of the same logic that the UpdateTask() uses. So we’ll first refactor some of the UpdateTask() method’s logic into private methods and then we’ll implement the GetTask() operation to use these private methods.

  1. Open the TaskService.cs file in the code editor.

  2. In the UpdateTask() method, select the entire “if” block in which the task id is parsed and right-click.  In the context menu that appears, select “Refactor”—>”Extract Method”.  In the “Extract Method” dialog box that appears, enter “ParseTaskId” as the name for the new method and press enter.  The new ParseTaskId() method should look like the following:

        private static int ParseTaskId(string id)
        {
            int parsedId;
            if (!int.TryParse(id, out parsedId))
            {
                throw new WebFaultException<string>(
                   string.Format(
                       "The value '{0}' is not a valid task id. The id must be an integer."
                       id), HttpStatusCode.BadRequest);
            }
            return parsedId;
        }

  3. Also in the UpdateTask() method, select the code within the “catch” block and right-click.  In the context menu that appears, select “Refactor”—>”Extract Method”.  In the “Extract Method” dialog box that appears, enter “ThrowNoSuchTaskId” as the name for the new method and press enter.  The new ThrowNoSuchTaskId() method should look like the following:

        private static void ThrowNoSuchTaskId(int parsedId)
        {
            throw new WebFaultException<string>(
               string.Format("There is no task with the id '{0}'.", parsedId),
               HttpStatusCode.NotFound);
        }

    Note: Both of these new private methods throw a WebFaultException.  The WebFaultException class is new in WCF WebHttp Services in .NET 4.  It was discussed in part five of this blog post series.

  4. With the new ParseTaskId() and ThrowNoSuchTaskId() methods, the implementation of the GetTask() operation is straightforward.  Copy the following code into the TaskService class:

        [Description("Returns the details of a single task.")]
        [WebGet(UriTemplate = "{id}")]
        public Task GetTask(string id)
        {
            int parsedId = ParseTaskId(id);
            using (TeamTaskObjectContext objectContext =
                      new TeamTaskObjectContext())
            {
                var task = objectContext.Tasks.FirstOrDefault(t => t.Id == parsedId);
                if (task == null)
                {
                    ThrowNoSuchTaskId(parsedId);
                }
                return task;
            }
        }

  5. You may notice that the GetTask() operation uses the same UriTemplate as the UpdateTask() operation.  The difference between the two operations is the associated HTTP method: the UpdateTask() operation uses a [WebInvoke] attribute with the method set to “PUT” while the GetTask() operation uses a [WebGet] attribute, which is always for HTTP GET requests.

  6. Start without debugging (Ctrl+F5) and use the browser of your choice to navigate to http://localhost:8080/TeamTask/Tasks/1.  In Internet Explorer, the single task will be displayed as shown below:

    SingleTaskInBrowser_sansUserNames

Step 4: Adding the GetUsers() Operation

Lastly, for the sake of completeness we’ll also add a GetUsers() operation that will return a list of users just like the GetTasks() operation returns a list of tasks.

  1. Open the UserService.cs file in the code editor and add the following implementation of the GetUsers() operation:

        [Description("Returns the users on the team.")]
        [WebGet(UriTemplate = "?skip={skip}&top={top}&manager={userName}")]
        public List<User> GetUsers(int skip, int top, string userName)
        {
            // Set reasonable defaults for the query string parameters
           
    skip = (skip >= 0) ? skip : 0;
            top = (top > 0) ? top : 25;

            using (TeamTaskObjectContext objectContext =
                      new TeamTaskObjectContext())
            {
                // Include the where clause only if a userName was provided
               
    var userQuery = (string.IsNullOrWhiteSpace(userName)) ?
                    objectContext.Users :
                    objectContext.Users.Where(
                        user => user.ManagerUserName == userName);

                return userQuery.OrderBy(user => user.UserName)
                                       .Skip(skip).Take(top).ToList();
            }
        }

    The GetUsers() operation is very similar to the GetTasks() operation introduced in the first part of this blog post series.  The only difference is that GetUsers() operation provides the ability to filter the list of users by the user’s manager whereas the GetTasks() operation provided the ability to filter by task owner.

  2. Start without debugging (Ctrl+F5) and use the browser of your choice to navigate to http://localhost:8080/TeamTask/Users?manager=user4.  In Internet Explorer, the list of users will be displayed as shown below: 

    ListOfUsersInBrowser_sansUserNames

Next Steps: Integrating ASP.NET Output Caching with WCF WebHttp Services

We performed a lot of code refactoring in this part of the blog post series.  But by starting to think of the TeamTask service in terms of resources, we’re in a much better position to add features and functionality to the service.  Involved in this refactoring was the use of the new ASP.NET routes integration feature introduced with WCF WebHttp Services in .NET 4.

In part seven of this blog post series we’ll take a look at another ASP.NET integration feature introduced with WCF WebHttp Services in .NET 4: declarative output caching.

Randall Tombaugh
Developer, WCF WebHttp Services

What does the persistence subsystem do?

The persistence subsystem is responsible for making a workflow instance durable. Durable workflow instances can be unloaded from memory and reloaded on the same or a different machine at a later point in time. This way, the persistence subsystem enables scenarios such as long-running workflow applications, increase system load by unloading idle instance, migration of instances between machines, scale out and load-balancing, and recovery of failed instances.

The persistence subsystem performs the following tasks:

·         Storing the state of workflow instances in a persistence store.

·         Recovery of durable workflow instances.

·         Activation of durable workflow instances with expired durable timers.

·         Instantaneous reactivation of durable workflow instances after a shutdown or crash of the Workflow Service Host.

Persistence for a workflow is enabled by defining an instance store for the Workflow Service Host. The .NET 4 framework comes with the SQL Workflow Instance Store, which is a SQL Server implementation of an instance store.

When a workflow instance persists, the SQL Workflow Instance Store (SWIS) saves the current instance state into the persistence store together with additional metadata that is required for activation, recovery and control. After the instance is persisted, the Workflow Service Host can unload the instance from memory. At a later point in time the Workflow Service Host may instruct the instance store to load the instance again. For example, the Workflow Service Host will automatically reload the workflow instance when a new message arrives for that instance or if any of the instance's timers expire.

Persistence is triggered in multiple ways:

·         Some activities persist the instance. The SendReplyToReceive activityhas a PersistBeforeSend property, which can be set to persist the Workflow state before a reply is sent.

·         The user defines the Workflow Idle behavior and sets the PersistOnIdle time. When specified the Workflow Service Host persists the instance after the instance has been idle for the specified time. An instance can go idle when it is waiting on a receive or delay activity.

·         The user defines the Workflow Idle behavior and sets the UnloadOnIdle time. When specified the Workflow Service Host persists and unloads the instance after the instance has been idle for the specified time.

In addition to these mechanisms, the workflow can contain explicit persistence activities. Those explicit persist activities are only required to persist a workflow throughout a long episode of computation or to guarantee that certain well-known persist points are present.

How does the persistence subsystem work?

The persistence subsystem consists of four parts:

1.       Persistence Framework API A set of persistent interfaces that define the contract with Workflow Service Host to persists its state. These enable you to build any type of persistence provider from database backed to say distributed in-memory cache.

2.       The SQL Workflow Instance Store (SWIS) implements the abstract class InstanceStore of the Persistence Framework API. This class is used by the Workflow Service Host to create, load, save and delete durable instance data.

3.       The SQL Server Persistence database stores all the durable instance state. It also stores the additional metadata that is used to activate and recover a service instance.

4.       The Workflow Management Service (WMS) is a Windows Service that activates a Workflow Service Host whenever there are unloaded instances with expired durable timers, instances that need to be reactivated after a graceful shutdown or instances that need to be recovered after a crash. The WMS is also involved in the execution of instance control commands, which will be covered in a future blog post.

All workflow instances that are hosted by a Workflow Service Host that configures the SQL Workflow Instance Store save their instance state in the SQL Server Persistence Database. In addition to the instance state binary blob, the persistence store holds the following:

The message correlation key allows the Workflow Service Host to correlate an incoming message to a service instance if that instance is not loaded.

The instance lock indicates whether the service instance is loaded.

The service deployment information defines how the service is deployed in a IIS/WAS-hosted environment. It consists of site name, application path, service path and service name. Note that the instance store does not contain any deployment information for self-hosted workflows, meaning that the WMS is specifically tailored for IIS hosted services.

Loading and locking

If a workflow instance is loaded by a Workflow Service Host the persistence store locks that instance, and the instance cannot be loaded by any other service host. If the service host unloads the service instance the lock is released, and the instance can be loaded by a different service host. The new service host may reside on a different machine. This means that a workflow instance can run on multiple machines throughout its lifetime, whenever activation messages arrive. It also means that you can build a machine farm without employing an intelligent message router that remembers which machine is running a particular workflow instance. Instead, the router routes a message to any of the machines in the farm. If the message is correlated to an existing workflow instance, the instance will be loaded by that machine. Casually speaking, the service instance follows the message.

Processing of expired timers

If a workflow instance is executing a delay activity at the time it persists the instance store stores the expiration time of the activity. At the time the activity expires the SQL Workflow Instance Store notifies the Workflow Service Host, which then loads the workflow instance and processes the expired delay activity. If multiple machines are available, the instance may be loaded on any of these machines.

If no Workflow Service Host is running on a particular machine the WMS will activate a host on that machine. This causes the instances of a particular workflow type to be distributed among all machines in the farm.

Instance recovery

If a service host has loaded a workflow instance the Workflow Service Host must renew the instance's lock on a regular basis. If not renewed on time, the lock expires. An expired lock indicates that the service host or the machine the service host ran on has crashed. In this case another Workflow Service Host will load the instance. If no other Workflow Service Host is running the WMS will activate a host.

If the Workflow Service Host shuts down (e.g., due to an app domain recycle) it releases the locks of all the service instances it has loaded.

0 Comments
Filed under: , ,

This is part five of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Handling Errors with the WebFaultException class

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

Step 1: Handling Non-Existent Users

In the very first post of this series, we implemented the GetUser() service operation.  The GetUser() service operation returns the details of a user given a username provided in the request URI. 

A precondition of the GetUser() operation is that the given username exists in the database.  But what happens when this precondition isn’t met?  Unfortunately, the implementation from the first blog post does something quite awkward: It returns a response with an HTTP status code of 200 along with an empty message body. 

Let’s improve the GetUser() operation to handle non-existent user errors in a manner more consistent with HTTP.  To do this, we’ll use the WebFaultException class to both set the HTTP status code to 404 (Not Found) and provide an indication of the error in the message body. 

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. Open the TeamTaskService.cs file in the code editor.

  3. In the GetUser() operation add the code below after the LINQ query for the user.  You’ll also need to add “using System.Net;” to the code file.

        if (user == null)
        {
            throw new WebFaultException<string>(
                string.Format("There is no user with the userName '{0}'.", userName),
                HttpStatusCode.NotFound);
        }

    There are both generic and non-generic versions of the WebFaultException.  The generic version is generally the more appropriate one to use, as it allows you to specify the exception details that will be serialized into the body of the response.  In this case, the exception details are just a string, but any type that can be serialized can be used.  We’re also setting the correct HTTP status code of 404 (Not Found).

    As you can see, correctly handling errors with a WCF WebHttp Service is as simple as throwing an exception.

Helpful Tip: If you’ve used WCF to build SOAP services, you may notice that the WebFaultException is very similar to the FaultException class.  This isn't just a coincidence. The WebFaultException actually derives from the FaultException class.  Therefore in the context of a SOAP service, the WebFaultException will behave as a FaultException—the detail will be serialized to the body of the SOAP message and the HTTP status code will be ignored as it isn’t relevant with SOAP.

 

Step 2: Viewing the Error on the Client

We could verify that our improved implementation of the GetUser() operation correctly handles non-existent users with the browser, but we'll write some client code to do this instead.  This is because we want to demonstrate one of the more exciting features of the WebFaultException class—that it composes with automatic and explicit format selection

With the WebFaultException, the detail of the exception that is serialized in the body of the response message will always be in the format (XML or JSON) that the client would have received had there not been an error.  If the client was expecting XML, the client will get the exception detail serialized as XML.  Likewise, if the client was expecting JSON, the client will get the exception detail serialized as JSON.

  1. Open the Program.cs file of the TeamTask.Client project in the code editor and copy the following static method into the Program class:

        static void GetUser(HttpClient client, string userName, string accepts)
        {
            Console.WriteLine("Getting user '{0}':", userName);
            using(HttpRequestMessage request = new HttpRequestMessage("GET",
                     "Users/"+ userName))
            {
                request.Headers.Accept.AddString(accepts);
                using (HttpResponseMessage response = client.Send(request))
                {
                    Console.WriteLine("  Status Code: {0}", response.StatusCode);
                    Console.WriteLine("  Content: {0}",
                        response.Content.ReadAsString());
                }
            }
            Console.WriteLine();
        }

    The static GetUser() method is very similar to the client code we wrote in part two of this blog post series.  The only thing to notice is that we are able to set the HTTP Accept header on the request.

  2. Now we’ll implement the Main() method to call GetUser() twice: once with an XML content type in the Accept header and a second time with a JSON content type.  Replace the Main() method implementation with the following code:

        using (HttpClient client = new HttpClient("http://localhost:8080/TeamTask/"))
        {
            GetUser(client, "noSuchUser", "application/xml");
            GetUser(client, "noSuchUser", "application/json");

            Console.ReadKey();
        }

  3. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    NoSuchUSerInConsole_sansUserNames

    Notice that there is no such user with the username “noSuchUser” and that the status code for both responses is 404 (Not Found).  With the first request, the response content is in XML.  With the second request, the response content is in JSON, which happens to be just a quoted string for this simple example.  

Step 3: Handling Errors in the UpdateTask() Operation

The UpdateTask() operation, which we added to the TeamTask service in the part three, could also benefit from some improved error handling.

First, in the current implementation of the UpdateTask() operation, we are parsing a UriTemplate path variable to get the id of the task to update.  We assume that parsing the id will always be successful, but this isn’t a valid assumption.  We’ll change the implementation of the UpdateTask() operation so that it returns an HTTP status code of 400 (Bad Request) when the request URI includes a id value that can’t be parsed as an integer.

Second, it is possible that a client could try to update a non-existent task.  In the current implementation, this error will surface as a OptimisticConcurrencyException, which is thrown from the ADO.NET Entity Framework code on trying to persist the updated task to the database.  This OptimisticConcurrencyException will result in an HTTP response with a status code of 400 (Bad Request) and HTML content that explains that the server encountered an error.  This is the default behavior for unhandled, non-WebFaultExceptions.  We’ll change this so that the UpdateTask() operation returns an HTTP status code of 404 (Not Found) when a client attempts to update a non-existent task.

  1. Open the TeamTaskService.cs file in the code editor.

  2. In the UpdateTask() operation replace the code for parsing the id with the following code:

        // The id from the URI determines the id of the task  
       
    int parsedId;
        if(!int.TryParse(id, out parsedId))
        {
            throw new WebFaultException<string>(
                string.Format(
                    "The value '{0}' is not a valid task id. The id must be an integer."
                    id), HttpStatusCode.BadRequest);
        }
        task.Id = parsedId;

  3. Wrap the call to SaveChanges() on the ObjectContext with a try-catch block that will properly handle the OptimisticConcurrencyException as shown below.  You’ll also need to add “using System.Data;” to the code file.

        try
       
    {
            objectContext.SaveChanges();
        }
        catch (OptimisticConcurrencyException)
        {
            throw new WebFaultException<string>(
                string.Format("There is no task with the id '{0}'.", parsedId),
                HttpStatusCode.NotFound);  
        }

  4. After implementing these error handling improvements, the UpdateTask() should look similar to the code below.  We’ll leave it as an exercise for the reader to verify that these error handling improvements behave as expected.

        [Description("Allows the details of a single task to be updated.")]
        [WebInvoke(UriTemplate = "Tasks/{id}", Method = "PUT")]
        public Task UpdateTask(string id, Task task)
        {
            // The id from the URI determines the id of the task  
           
    int parsedId;
            if (!int.TryParse(id, out parsedId))
            {
                throw new WebFaultException<string>(
                    string.Format(
                        "The value '{0}' is not a valid task id. The id must be an integer."
    ,
                         id), HttpStatusCode.BadRequest);
            }
            task.Id = parsedId;

            using (TeamTaskObjectContext objectContext =
                      new TeamTaskObjectContext())
            {
                objectContext.AttachTask(task);
                try
               
    {
                    objectContext.SaveChanges();
                }
                catch (OptimisticConcurrencyException)
                {
                    throw new WebFaultException<string>(
                        string.Format("There is no task with the id '{0}'.", parsedId),
                        HttpStatusCode.NotFound);  
                }
                // Because we allow partial updates, we need to refresh from the dB
               
    objectContext.Refresh(RefreshMode.StoreWins, task);
            }

            return task;
        }

Next Steps: Using Routes to Compose WCF WebHttp Services

The TeamTask service can now handle possible client errors such as trying to update or retrieve non-existent tasks or users.  Of course, there are still opportunities to improve the error handling of the service but we'll leave such improvements as an exercise for the reader.  

In part six of this blog post series we’ll continue to improve the TeamTask service.  We’ll refactor our single TeamTaskService class into two separate classes (TaskService and UserService) as we learn about the ability to compose WCF WebHttp Services with the new ASP.NET routes integration feature introduced in .NET 4.

Randall Tombaugh
Developer, WCF WebHttp Services

This is part four of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Configuring automatic format selection for a WCF WebHttp Service
  • Setting the response format explicitly in code per request

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

 

Step 1: Handling Requests in JSON

So far in this blog post series we’ve been sending and receiving HTTP messages with XML content only.  But WCF WebHttp Services also provides first-class support for JSON.  In fact, by default a WCF WebHttp Service will handle requests in JSON automatically.  Just to prove this, we’ll have our client send an HTTP PUT request to the TeamTask service in JSON.

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. Open the Program.cs file from the TeamTask.Client project in the code editor and add the following static method to the Program class:

        static void WriteOutContent(HttpContent content)
        {
            content.LoadIntoBuffer();
            Console.WriteLine(content.ReadAsString());
            Console.WriteLine();
        }

    This static WriteOutContent() method will allow us to easily write out the body of a request or response message to the console.  The content is streamed by default, so we call the LoadIntoBuffer() method to give us the ability to read the content multiple times.

  3. Change the UpdateTask() method to serialize the request to JSON instead of XML by replacing:

        HttpContent content = HttpContentExtensions.CreateDataContract(task);

    With:

        HttpContent content = HttpContentExtensions.CreateJsonDataContract(task);

  4. Add a call to the WriteOutContent() method after the request content is created so that we can verify the request really is in JSON like so:

        HttpContent content = HttpContentExtensions.CreateJsonDataContract(task); 
        Console.WriteLine("Request:"); 
        WriteOutContent(content);

  5. Open the Web.Config file from the TeamTask.Service project and change the value of the “automaticFormatSelectionEnabled” setting from “true” to “false” like so:

        <webHttpEndpoint>
           
    <standardEndpoint name="" helpEnabled="true
                                        automaticFormatSelectionEnabled="false"/>
        </
    webHttpEndpoint>

    Note: The default value for automaticFormatSelectionEnabled is actually false, so we could simply delete the attribute altogether.  However, we’ll be re-enabling it soon so it is easier to keep it around.

  6. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    JsonRequestInConsole_sansUserName

    Notice that the HTTP PUT request is in JSON, and yet the TeamTask service handles it without any problem.

    Note:  Throughout this blog post, we’ll be running both the service and the client projects together a number of times. You might find it convenient to configure the TeamTask solution to automatically launch the server and then the client when you press (F5).  To do this, right click on the TeamTask solution in the Solution Explorer (Ctrl+W,S) and select “Properties” from the context menu that appears.  In the Solution Properties window, select the “Multiple startup projects” radio button.  Make sure that the TeamTask.Service project is the top project in the list by selecting it and clicking on the up arrow button.  Click on the “Action” cell for the TeamTask.Service project and select “Start without Debugging”.  Then click on the “Action” cell for the TeamTask.Client project and select “Start”.

Helpful Tip: It might seem counterintuitive that we set “automaticFormatSelectionEnabled” to false when we were explicitly trying to demonstrate how our service could automatically handle a JSON request.  You would probably assume that “automaticFormatSelectionEnabled” needs to be set to “true” to handle a JSON request.  But this isn’t the case, as we saw when we executed the client.  The “automaticFormatSelectionEnabled” setting applies to outgoing responses only.  All incoming requests in either XML or JSON are automatically handled, and in fact, there is no way to disable this behavior to accept only one format or the other.

 

Step 2:  Setting the Response Format Automatically

In step one, we wrote the request message body to the console to show that it was in fact JSON.  But what about the response message?  We didn’t write it out to the console, but it happens to be in XML.  You can see for yourself by adding a call to the WriteOutContent() method and passing in the response content. 

Of course this is odd—a client sends JSON and gets XML back.  This occurs because the default response format of the UpdateTask() operation is XML.  You specify the default response format for an operation using the ResponseFormat property on the [WebGet] or [WebInvoke] attributes, but the ResponseFormat property isn’t explicitly set with the UpdateTask() operation and it defaults to XML.

We could set the ResponseFormat for the UpdateTask() operation to JSON, but that wouldn’t really solve our problem.  If a client were to send a request in XML, it would get back a response in JSON.  What we really want is for the WCF infrastructure to be smart enough to choose the correct response format given the request itself.  And in fact, this is exactly the behavior the new automatic format selection feature in WCF WebHttp Services provides. 

Enabling automatic format selection is as easy as setting “automaticFormatSelectionEnabled” to “true” in the Web.config for the service.  With the online templates it is set to “true” by default, which is why we had to set it to “false” in step one.

When automatic format selection is enabled, the WCF infrastructure will try to determine the appropriate response format using:

  1. The value of the HTTP Accept header of the request.  If the request doesn’t provide an Accept header or the Accept header doesn’t list an appropriate format, the WCF infrastructure will try to use…

  2. The content-type of the request.  If the request doesn’t have a body or if the content-type of the request isn’t an appropriate format, the WCF infrastructure will use…

  3. The default response format for the operation.

Now we’ll re-enable automatic format selection and demonstrate how it works by sending an HTTP GET request with an Accept header indicating a preference for JSON.  We’ll also update our client to write out the response from the HTTP PUT request and show that the response format matches that of the request (even without an Accept header).

  1. Open the Web.Config file from the TeamTask.Server project and change the value of the “automaticFormatSelectionEnabled” setting from “false” to “true” like so:

        <webHttpEndpoint>
           
    <standardEndpoint name="" helpEnabled="true
                                        automaticFormatSelectionEnabled="true"/>
        </
    webHttpEndpoint>

  2. Open the Program.cs file from the TeamTask.Client project in the code editor and replace the static GetTask() method implementation with the implementation below.  You’ll also need to add “using System.Runtime.Serialization.Json;” to the code file:

        static Task GetTask(HttpClient client, int id)
        {
            Console.WriteLine("Getting task '{0}':", id);
            using (HttpRequestMessage request =
                      new HttpRequestMessage("GET", "Tasks"))
            {
                request.Headers.Accept.AddString("application/json");
                using (HttpResponseMessage response = client.Send(request))
                {
                    response.EnsureStatusIsSuccessful();
                    WriteOutContent(response.Content);
                    return response.Content.ReadAsJsonDataContract<List<Task>>()
                                           .FirstOrDefault(task => task.Id == id);
                 }
            }
        }

    This implementation of the static GetTask() method is very similar to the client code we wrote in part two of this blog post series.  However, we are now instantiating an HttpRequestMessage so that we can set the Accept header to “application/json”.  We are also using the ReadAsJsonDataContract() extension method instead of the ReadAsDataContract() extension method since the content is now JSON instead of XML.

  3. If you try to build you’ll find that the ReadAsJsonDataContract() extension method has a dependency, so in the “Solution Explorer” window, right click on the TeamTask.Client project and select “Add Reference…”.  In the “Add Reference” window, select the “.NET” tab and choose the System.ServiceModel.Web assembly.

  4. In the UpdateTask() method, also write the response out to the console and replace the ReadAsDataContract() call with a ReadAsJsonDataContract() call like so:

        using (HttpResponseMessage response = client.Put(updateUri, content)) 
        { 
            response.EnsureStatusIsSuccessful(); 
            Console.WriteLine("Response:"); 
            WriteOutContent(response.Content); 
            return response.Content.ReadAsJsonDataContract<Task>();              
        }

  5. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    AutomaticFormatSelectionOutputInConsole_sansUserNames

    Notice that JSON was used for the responses of both the GET and PUT requests.  JSON was used with the GET request because of the HTTP Accept header value, and it was used for the PUT request because the request itself used JSON.

Step 3: Setting the Response Format Explicitly

Automatic format selection is a powerful new feature, but it may not be the solution for all scenarios.  For example, it won’t work when a service wants to support clients that aren’t able to set the HTTP Accept header when they send GET requests.  Therefore WCF WebHttp Services also allows the response format to be set explicitly in code.

We’ll demonstrate how to explicitly set the response format by adding a “format” query string parameter to the GetTasks() operation of the TeamTask service.  The value of this query string parameter will determine the response format.

  1. Open the TeamTaskService.cs file in the code editor.

  2. Add a “format” query string parameter to the UriTemplate of the GetTasks() operation along with a matching method parameter like so:

        [WebGet(UriTemplate =
            "Tasks?skip={skip}&top={top}&owner={userName}&format={format}")] 
        public List<Task> GetTasks(int skip, int top, string userName, string format)

  3. Within the body of the GetTasks() operation, add the following code:

       // Set the format from the format query variable if it is available
      
    if (string.Equals("json", format, StringComparison.OrdinalIgnoreCase))
       {
           WebOperationContext.Current.OutgoingResponse.Format =
                                                                                 WebMessageFormat.Json; 
       }

    Setting the response format explicitly is as simple as setting the Format property on the OutgoingResponse of the WebOperationContext.  When the “format” query string is equal to “json”, the response format is set to JSON, otherwise the default value of XML is used.

  4. Start without debugging (Ctrl+F5) and use the browser of your choice to navigate to http://localhost:8080/TeamTask/Tasks.  In Internet Explorer, the list of tasks will be displayed as shown below:

    XmlFormatInBrowser_sansUserNames

  5. Now, send another request with the browser, but add the “format” query string parameter to the URI like so: http://localhost:8080/TeamTask/Tasks?format=json.  In Internet Explorer, you’ll be presented with a dialog box asking if you want to download the response as a file.  Save the file and open it in notepad and it will contain the tasks in the JSON format as shown below:

    JsonFormatInNotepad_sansUserNames

Helpful Tip: While WCF WebHttp Services offers first-class support for XML and JSON, it also provides a more advanced API for returning plain text, Atom feeds, or any other possible content-type.  We’ll cover this feature in part eight of this blog post series.

 

Next Steps: Error Handling with WebFaultException

So far in our implementation of the TeamTask service, we haven’t provided any real error handling.  In part five of this blog post series we’ll rectify this and use the new WebFaultException class to communicate why a request has failed by providing an appropriate HTTP status code.

Randall Tombaugh
Developer, WCF WebHttp Services

This is part three of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Creating operations for updating server state with the [WebInvoke] attribute
  • Persisting changes with the ADO.NET Entity Framework in a web service scenario

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

 

Step 1: Adding an Operation to Update Tasks

In part one of this of this blog post series, we used the [WebGet] attribute to indicate that a given method was a service operation.  By specifying a UriTemplate value on the [WebGet] attribute, we further indicated that the given service operation should handle all HTTP GET requests when the request URI matches the UriTemplate.  However, we didn’t mention in part one that there is a second attribute used to specify service operations: the [WebInvoke] attribute.

The [WebInvoke] attribute is just like the [WebGet] attribute, except that with the [WebInvoke] attribute we are able to specify the HTTP method that the service operation should handle.  With the [WebGet] attribute, the HTTP method is always implicitly an HTTP GET.  Therefore, we’ll use the [WebInvoke] attribute to create a service operation for updating a given task.

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. Open the TeamTaskService.cs file in the code editor and copy the code below into the TeamTaskService class.  You’ll need to add “using System.Data.Objects;” to the code file:

        [Description("Allows the details of a single task to be updated.")] 
        [WebInvoke(UriTemplate = "Tasks/{id}", Method = "PUT")] 
        public Task UpdateTask(string id, Task task) 
        { 
            // The id from the URI determines the id of the task 
           
    int parsedId = int.Parse(id);           
            task.Id = parsedId; 
                
            using (TeamTaskObjectContext objectContext =
                      new TeamTaskObjectContext()) 
            {  
                objectContext.AttachTask(task);  // We’ll need to implement this!
                objectContext.SaveChanges();
                // Because we allow partial updates, we need to refresh from the dB 
               
    objectContext.Refresh(RefreshMode.StoreWins, task);   
           


            return task; 
        }

    Notice that we used the [WebInvoke] attribute and specified the HTTP method to be “PUT”.  Also notice that we included a [Description] attribute for the automatic help page as we did in part two of this blog post series.

    The UpdateTask() method has two input parameters: id and task.  The id parameter value will  come from the URI of the request because of the matching {id} variable in the UriTemplate.  The task parameter is not mapped to any variables in the UriTemplate, so its value will come from the body of the request.

    In the implementation of the UpdateTask() method we need to parse the id parameter because it is of type string, but task id’s are of type int.  Obviously, this call to int.Parse() could throw an exception, but for now we’ll simply let the exception bubble up.  In part five of this blog post series we’ll add proper exception handling to ensure that a more appropriate HTTP response is returned if the parsing fails.

    One final subtlety to explain is the call to the Refresh() method, which will update the task instance with the current values from the database.  It might seem odd that we have to make this call given that we just persisted the same task instance to the database.  However, we are doing this because we will allow clients to make partial updates.  The task supplied in the request may only have values for the properties that are to be updated.  However, the task provided in the response should have all of the properties of the task, so we must get these property values from the database.  A custom stored procedure could be used to optimize this, but we won’t explore that option here.

  3. If we build the TeamTask.Service project as it is now, it will fail because the TeamTaskObjectContext doesn’t contain a definition for the AttachTask() method.  We’ll implement the AttachTask() method in the next step.

Step 2: Creating a mechanism for Attaching Tasks

As you may recall from part one of this blog post series, we are using the ADO.NET Entity Framework to move data between CLR instances and the database.  The ADO.NET Entity Framework uses an ObjectContext to cache the CLR instances locally and track changes made to the them.  To persist these local changes to the database, a developer calls the SaveChanges() method on the ObjectContext. 

However, with a web service like the TeamTask service, the instances that need to be persisted to the database usually aren’t being tracked by the ObjectContext because they’ve just been deserialized from the HTTP request.  Therefore, we need to inform the ObjectContext of the instances that we want to persist to the database and indicate which properties on the instances have new values. 

To do the work of informing the ObjectContext of our updated task, we’ll add an AttachTask() method to our custom ObjectContext, the TeamTaskObjectContext class.   We’ll also add a GetModifiedProperties() method to the Task class that will be responsible for indicating which properties on the task have new values.

  1. Open the TeamTaskObjectContext.cs file from the Model folder of the TeamTask.Service project in the code editor and copy the code below into the TeamTaskObjectContext class:

        public void AttachTask(Task task)
        {
            this.Tasks.Attach(task);
            ObjectStateEntry entry =
                this.ObjectStateManager.GetObjectStateEntry(task);
            foreach (string propertyName in task.GetModifiedProperties())
            {
                entry.SetModifiedProperty(propertyName);
            }
        }

     

    The AttachTask() method uses an ADO.NET Entity Framework class called the ObjectStateManager to get an ObjectStateEntry for the task, and then sets the modified properties on the entry.  The list of modified properties is supplied by the task itself in the GetModifiedProperties() method, which we’ll need to implement next.

  2. Open the Task.cs file in the code editor and copy the code below into the Task class.  You’ll need to add “using System.Collections.Generic;” to the code file:

        internal IEnumerable<string> GetModifiedProperties()
        {
            // Create the list with the required properties
           
    List<string> modifiedProperties = new List<string>() { "TaskStatusCode"
                                                                                         "OwnerUserName",
                                                                                         "Title" };
            // Add the optional properties 
           
    if (!string.IsNullOrWhiteSpace(this.Description)) 
            { 
                modifiedProperties.Add("Description"); 
            } 
            if (this.CompletedOn.HasValue) 
            { 
                modifiedProperties.Add("CompletedOn"); 
            } 
            return modifiedProperties; 
        }

    The GetModifiedProperties() simple returns a list of property names.  Notice that the Id and CreatedOn properties of the Task class will never be included in this list, indicating that these properties can never be updated.  We obviously don’t want to update the Id value of a task since we are using it for identity purposes.  A user might try to update the CreatedOn value of a give task by sending an HTTP PUT request with CreatedOn value supplied, but the value will always be ignored.

Helpful Tip: Web services like the TeamTask service are almost always a tier of an N-Tier application. If your interested in learning more about building N-Tier applications with WCF and the ADO .NET Entity Framework in .NET 4, Daniel Simmons from the Entity Framework Team has written some informative MSDN Magazine articles here and here.

 

Step 3: Updating Task Status with the HttpClient

Now that we’ve implemented the UpdateTask() operation, lets write some client code to update the status of one of the tasks.  We already covered the basics of using the HttpClient in part two of this blog post series, so we’ll go through the client code quickly.

  1. Open the Program.cs file of the TeamTask.Client project in the code editor and copy the following static method into the Program class:

        static Task GetTask(HttpClient client, int id) 
        { 
            Console.WriteLine("Getting task '{0}':", id); 
            using (HttpResponseMessage response = client.Get("Tasks")) 
            { 
                response.EnsureStatusIsSuccessful(); 
                return response.Content.ReadAsDataContract<List<Task>>() 
                                       .FirstOrDefault(task => task.Id == id); 
            } 
        }

    The static GetTask() method is very similar to the client code we wrote in part two.  The GetTask() method uses the ReadAsDataContract() extension method to get a typed list of tasks from the TeamTask service response and then returns a task from the the list with a given id.

  2. Also copy the following static method into the Program class:

        static Task UpdateTask(HttpClient client, Task task) 
        { 
            Console.WriteLine("Updating task '{0}':", task.Id); 
            Console.WriteLine(); 

            string updateUri = "Tasks/" + task.Id.ToString(); 
            HttpContent content = HttpContentExtensions.CreateDataContract(task); 
                    
            using (HttpResponseMessage response = client.Put(updateUri, content)) 
            { 
                response.EnsureStatusIsSuccessful(); 
                return response.Content.ReadAsDataContract<Task>(); 
            } 
        }

    The static UpdateTask() method is slightly more interesting than the GetTask() method since we have to provide a message body with the request.  The Put() extension method for the HttpClient takes both a URI argument and a content argument.  The URI includes the id of the task to be updated and the content is just a task instance that has been serialized using the static CreateDataContract() method.

  3. Copy this last static method into the Program class so that we can write out the state of a task to the console:

        static void WriteOutTask(Task task) 
        { 
            Console.WriteLine("  Id:      {0}", task.Id); 
            Console.WriteLine("  Title:   {0}", task.Title); 
            Console.WriteLine("  Status:  {0}", task.Status); 
            Console.WriteLine("  Created: {0}", task.CreatedOn.ToShortDateString()); 
            Console.WriteLine(); 
        }

  4. Now we’ll implement the Main() method to retrieve a task, update the status of the task, and then retrieve the task again to verify that the change was persisted to the database.  Replace any code within the Main() method with the following code:

        using (HttpClient client = new HttpClient("http://localhost:8080/TeamTask/"))
        { 
            // Getting task 1 
           
    int taskId = 1
            Task task1 = GetTask(client, taskId); 
            WriteOutTask(task1); 

            // Update task 1 
           
    task1.Status = TaskStatus.InProgress; 
            task1.CreatedOn = new DateTime(2009, 12, 1); 
            Task task1Updated = UpdateTask(client, task1); 
            WriteOutTask(task1Updated); 

            // Get task 1 again to see the updated status 
           
    Task task1Again = GetTask(client, taskId); 
            WriteOutTask(task1Again); 

            Console.ReadKey(); 

            // Return the dB to its original state 
           
    task1.Status = TaskStatus.Completed; 
            UpdateTask(client, task1); 
         }

    Notice that when we update the task1 status, we are also updating the task1 creation date.  However, you’ll recall that in the GetModifiedProperties() method on the Task class, we never set the CreatedOn property as modified, so we shouldn’t see the new creation date in the response from the update or when we retrieve the task a second time. 

  5. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    ClientOutputInConsole

    Notice that the task status is updated from “Completed” to “InProgress”, but that the creation date remains unchanged.

Next Steps: Automatic and Explicit Format Selection in WCF WebHttp Services

Our TeamTask service development is coming along nicely.  The TeamTask service now supports querying for tasks, retrieving users, and partial updates of tasks.  We also have a client that can programmatically access the service.

However, you may have noticed that all of the responses we’ve received from the TeamTask service have been in XML.  In part four of this blog post series, we’ll see how easy it is to also generate JSON responses when we take a look at automatic and explicit format selection in WCF WebHttp Services.

Randall Tombaugh
Developer, WCF WebHttp Services

Welcome to 2010!

Over the past week, we posted four new documents into the WF4 Migration Guidance series. We also added new sample code up to the WF4 Migration Guidance Code Gallery page around Workflow Services migration guidance.

Revisiting the structure I used in the Beta 1 migration guidance post last summer, the list looks like the following:

  • WF Migration Overview (Already Live; Updated to Beta 2)
    Overview of the document collection and an initial starting place for the WF3 developer
  • WF Migration: Best Practices for WF3 Development (Already Live; Updated to Beta 2)
    How to design WF3 artifacts so they are more easily migratable to WF4
  • WF Guidance: Rules (Already Live; Updated to Beta 2)
    Discussion of how to bring rules-related investments forward into .NET 4
  • WF Guidance: State Machine (Already Live; Updated to Beta 2)
    Discussion of WF4 control flow modeling in the absence of a StateMachine activity
  • WF Guidance: Workflow Services (Updated Today)
    Discussion of implementing workflow services using WCF
  • WF Migration Cookbook: Custom Activities (Already Live; Updated to Beta 2)
    Examples and instructions for redesigning WF3 custom activities on WF4
  • WF Migration Cookbook: Workflows (Already Live; Updated to Beta 2)
    Examples and instructions for redesigning WF3 workflows on WF4
  • WF Migration Cookbook: Workflow Hosting (Now Live!)
    Examples and instructions for redesigning WF3 workflow hosting code on WF4
  • WF Migration Cookbook: Workflow Tracking (Now Live!)
    Examples and instructions for redesigning WF3 workflow tracking code on WF4
  • WF Migration Cookbook: Advanced Custom Activities (Now Live!)
    Examples and instructions for redesigning advanced WF3 custom activities on WF4

As I mentioned in my initial post about the documents, these documents are draft releases that the team is releasing to you. For these, they are all written for the object model and features that are current as of Beta 2. As we did from Beta 1 –> Beta 2, the team will continue to update the documents as we come up on RTM later this year, with the documents being expanded to address further scenarios.

Also – again – the team will be supporting feedback and requests for the documents and accompanying sample code in the WF 4 forum on MSDN.

Happy reading!
Cliff

0 Comments
Filed under: , ,

By now you may have read about the exciting new capabilities in Windows Server AppFabric. In this post we will dive deeper into the features that enable monitoring and troubleshooting of your WCF and WF applications. The monitoring capabilities for WCF and WF applications in AppFabric are built using the monitoring enhancements in .NET Framework 4. The WCF and WF runtime have been instrumented to emit tracing and tracking events to a high performant Event Tracing for Windows (ETW) session. ETW allows turning on monitoring by default, since it has minimal impact on the application performance.

The AppFabric monitoring infrastructure is built using the following events that are emitted from the runtime:

· Analytic Tracing Events: Analytic traces are targeted traces that get emitted from the WCF runtime, at key execution points such as operation completion, service error

· Workflow Tracking Events: Workflow tracking events are emitted during the execution of a workflow instance. These events provide visibility into the workflow execution such as when a workflow instance starts or encounters an error

· Message Flow Tracing: Turning on message flow tracings allows correlation of traces between different services. This allows reconstruction of end to end message flows between services deployed on a single machine or distributed across machines

Now that we have briefly discussed the events that are leveraged by Windows Server AppFabric monitoring let us understand how these events enable monitoring and troubleshooting. AppFabric monitoring includes an Event Collector service that listens for the tracing and tracking events. The Event Collector service collects these events and stores them to a monitoring database. The AppFabric tooling queries the monitoring databases to display monitoring and troubleshooting data. The figure below is a high level view of monitoring components:

clip_image002[6]

The verbosity of events emitted is controlled by the monitoring level in AppFabric. The monitoring level can be changed depending on the situation such as if the service encounters errors; the verbosity of events can be increased to help troubleshooting. The monitoring levels in AppFabric are:

· ErrorsOnly: Events are collected only if service encounters an error

· HealthMonitoring: The default level that allows AppFabric tooling display health of WCF and WF applications

· End To End Monitoring: Enables message flow tracing to correlate events between services.

· Troubleshooting: The most verbose level to diagnose issues with your WCF and WF service.

A future post will talk about AppFabric configuration and how the monitoring levels can be changed for a WCF or WF application.

We will show an example of troubleshooting a WF service deployed in AppFabric. We will use a simple WF service, from the WF tracking samples. The sample workflow is a simple workflow that computes reciprocal of the input. An error is simulated using an input of zero. Deploy the application to AppFabric as mentioned in the post.

We will use AppFabric tooling to troubleshoot the service once the error has been simulated. To monitor the health of an application a user will usually open the AppFabric Dashboard. The dashboard shows metrics related to the deployed WCF and WF services. In this case, the service encountered an error; hence the failed WCF call and the failed WF instance have been highlighted.

clip_image004[6]

When a WF service executes you see WF tracking events corresponding to the WF execution and WCF events corresponding to the execution of the messaging activities. When the service invocation fails you will get both a WCF exception event, corresponding to failure in execution of messaging activity (Receive activity) and a WF instance failure event. If you click on the exception in WCF Call History, you will get the details of the exception.

clip_image006[6]

For the workflow instance you may want to know which activity failed. Click on the failures in the WF Instance History in the dashboard. Right click on the aborted WF and view the tracked events for the WF

clip_image008[6]

clip_image010[6]

The source of the error is the Assign activity. The exception stack can be found out by clicking on the Errors tab in the details pane.

This shows you how the analytic tracing and WF tracking helps troubleshoot a WCF or WF service. To correlate traces from WCF and WF events you will need to change the monitoring level of the application to End-to-End Monitoring. This uses the message flow feature mentioned earlier to add an end to end activity id to the trace events to navigate between WCF and WF events originating as a part of a request.

We have seen how the monitoring infrastructure enhancements and Windows Server AppFabric tooling make it easier to monitor and troubleshoot your WCF and WF applications. To summarize

· The .NET Framework 4 emits high performant tracing and tracking events to ETW which can be enabled with minimal impact on the application

· Windows Server AppFabric tooling helps you visualize the events to gauge the health or troubleshoot problems with your application

· Windows Server AppFabric configuration gives you control on the verbosity of the events through monitoring levels

0 Comments
Filed under:

This is part two of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

  • Using the HttpClient from the WCF REST Starter Kit Preview 2
  • Browsing the Automatic Help Page of a WCF WebHttp Service

Over the course of this blog post series, we are building a web service called TeamTask.  TeamTask allows a team to track tasks assigned to members of the team.  Because the code in a given blog post builds upon the code from the previous posts, the posts are intended to be read in-order.

Downloading the TeamTask Code

At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

Getting Visual Studio 2010

To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

 

Step 1: Creating the TeamTask.Client Project

At the end of part one of the this blog series we had created the TeamTask service and exposed two HTTP GET operations: one for retrieving a list of tasks and another for retrieving a single user.

We’ll now add a simple console project to our solution to use for our client.

  1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.

  2. In the “Solution Explorer” window (Ctrl + W, S), right click on the TeamTask solution and select “Add”—>”New Project”. This will open the “New Project” dialog window.

  3. On the left-hand side of the “New Project” dialog window select “Installed Templates” and then select “Visual C#”—>”Windows” from the tree-view control.  Choose the “Console Application” project template.

  4. For the project name enter “TeamTask.Client” and then click “Ok”.  This will create the TeamTask.Client project in the solution.

Step 2: Adding the HttpClient to the TeamTask.Client Project

There are numerous ways to consume a WCF WebHttp Service like the TeamTask service.  You could use the WCF WebChannelFactory, the HttpWebRequest class, or even the WebClient class.  However, we recommend using the HttpClient.

The HttpClient is part of a client-side library that was included with the WCF REST Starter Kit Preview 2.  In order to use the HttpClient we’ll download the WCF REST Starter Kit Preview 2 and include the Microsoft.Http.dll and Microsoft.Http.Extensions.dll libraries in our client project.

We’ll also need to change the target framework for the TeamTask.Client project because the default for console applications is “.NET Framework 4 Client Profile” but the HttpClient has dependencies on assemblies in the full .NET 4 profile.

  1. In the browser of your choice, navigate to WCF REST Starter Kit Preview 2 download page.
  2. Click on the WCF REST Starter Kit Preview 2.msi download link and follow the prompts to install the WCF REST Starter Kit Preview 2.

  3. In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Add Reference…”.  In the “Add Reference” window, select the “Browse” tab and select the Microsoft.Http.dll and Microsoft.Http.Extensions.dll libraries under:

        %ProgramFiles%\Microsoft WCF REST\WCF REST Starter Kit Preview 2\Assemblies

  4. In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Properties”.  On the “Applications” tab of the “Properties” editor, change the target framework from “.NET Framework 4 Client Profile” to “.NET Framework 4”.

Helpful Tip: If you would like to learn more about the HttpClient, there is a useful two-part screencast on Channel 9: part 1 & part 2.

 

Step 3: Writing Tasks to the Console

To demonstrate how easy it is to use the HttpClient to get data from our TeamTask service, we’ll retrieve a list of tasks from the service and write out the list to the console.  Later, we’ll see more advance methods for handling request and response content with the HttpClient.

  1. Open the Program.cs file from the TeamTask.Client project in the code editor and copy the code below into the Main() method.  You’ll also need to add “using Microsoft.Http;” to the code file.

        using (HttpClient client = new HttpClient("http://localhost:8080/TeamTask/")) 
        { 
            // Getting the response as a string 
           
    Console.WriteLine("The tasks owned by user3:"); 
            using(HttpResponseMessage response = client.Get("Tasks?owner=user3")) 
            { 
                response.EnsureStatusIsSuccessful(); 
                Console.WriteLine(response.Content.ReadAsString()); 
            } 
            Console.ReadKey(); 
         }

    The HttpClient has a constructor overload that takes a base address.  This allows you to use relative URIs with all of the requests made with that given instance of the HttpClient.  This can be very convenient when you’ll only be making requests to a single domain.

    Also, the Microsoft.Http.Extensions.dll contains extension methods for the common HTTP methods GET, PUT, POST, DELETE and HEAD.  These extension methods use the more general Send() method of the HttpClient.  The Get() method used in the code above is one of these extension methods. 

    Lastly, the HttpClient does not throw an exception for non-successful requests (HTTP status codes other than 2xx).  If you want to throw an exception if the request was not successful, you need to call the EnsureStatusIsSuccessful() method.

  2. Start without debugging (Ctrl+F5) to start the TeamTask service.  Note: If you have build errors because the “Microsoft.Http” namespace doesn’t exist, make sure you have updated the TeamTask.Client project target framework version to use the full profile as explained in Step 2 above.

  3. Start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    TasksForUser3InConsole

  4. While this output is correct, the lack of formatting makes the XML difficult to read.  For XML content, a simple trick you can use to get better formatting is to use the ReadAsXElement() extension method for the HttpContent class.  Add “using System.Xml.Linq;” to the code file and then change the call to ReadAsString() like so:

        Console.WriteLine(response.Content.ReadAsXElement().ToString());

  5. Again, start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:  
    TasksForUser3InConsoleFormatted

 

Step 4: Iterating over the Tasks Using LINQ to XML

In the previous step, we used the ReadAsXElement() extension method to easily format our XML content.  However, working with the response content as an XElement tree allows us to do much more.  We’ll use the ReadAsXElement() extension method and LINQ to write just the task titles to the console.

  1. Copy the following code into the Main() method within the HttpClient “using” block:

        // Getting the titles using LINQ to XML 
       
    Console.WriteLine("The titles of the current tasks:"); 
        XNamespace ns =
            "http://schemas.datacontract.org/2004/07/TeamTask.Model"
        using(HttpResponseMessage response = client.Get("Tasks")) 
        { 
            response.EnsureStatusIsSuccessful(); 
            XElement tasks = response.Content.ReadAsXElement(); 
            var taskTitles = tasks.Descendants(ns + "Title"
                                          .Select(element => element.Value); 
            Console.WriteLine(string.Join(", ", taskTitles)); 
        } 
        Console.ReadKey();

    If you are familiar with the LINQ to XML APIs, then the above code should be easy to understand.  The only thing to be aware of is that the XML returned from the TeamTask service will have a default XML namespace, and in order to use the Descendants() method to filter all but the “Title” elements, the XNamespace has to be supplied.

  2. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:TaskTitlesInConsole

Step 5: Retrieving Tasks as Task Instances

Working with the response content as XML is useful, but being able to work with response content as strongly-typed CLR instances would be even better.  Of course, to work with the response content as strongly-typed instances, you would need to have the relevant class definitions available to your client project. 

For the sake of simplicity, we’ll simply add a reference to the TeamTask.Service project from our TeamTask.Client project in order to use the Task class.  However, referencing the service assembly like this might not always be an option. 

There are a couple of methods you could use to create classes for the HTTP message content.  The most obvious method would be to hand-code the classes, but this is a painful and error-prone process.  It would be preferable to generate the classes from a schema using a tool such as xsd.exe.  As we’ll see in step 6 below, WCF WebHttp Services provides an automatic help page that makes it easy to distribute such schema for the types used in your service.

  1. In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Add Reference…”.  In the “Add Reference” window, select the “Projects” tab and choose the TeamTask.Service project.

  2. Open the Program.cs file in the code editor if it isn’t already open.

  3. Add “using TeamTask.Model;” to the code file.

  4. We’ll use the ReadAsDataContract() extension method in the Microsoft.Http.Extensions.dll, but it has a dependency on the System.Runtime.Serialization assembly, which we need to add to our project.  In the “Solution Explorer” window, right click on the TeamTask.Client project and select “Add Reference…”.  In the “Add Reference” window, select the “.NET” tab and choose the System.Runtime.Serialization assembly. 

  5. Add “using System.Runtime.Serialization;” to the Program.cs file.

  6. Copy the following code into the Main() method within the HttpClient “using” block:

        // Getting the response as a strongly-typed List<Task>
       
    Console.WriteLine("The task owners:");
        using(HttpResponseMessage response = client.Get("Tasks"))
        {
            response.EnsureStatusIsSuccessful();
            List<Task> tasks = response.Content.ReadAsDataContract<List<Task>>();
            var taskOwners = tasks.Select(task => task.OwnerUserName);
            Console.WriteLine(string.Join(", ", taskOwners));
        }
        Console.ReadKey();

  7. Start without debugging (Ctrl+F5) to get the TeamTask service running and then start the client by right clicking on the TeamTask.Client project in the “Solution Explorer” window and selecting “Debug”—>”Start New Instance”.  The console should contain the following output:

    TaskOwnersInConsole_sansUserNames

Helpful Tip: The WCF REST Starter Kit Preview 2 includes a useful Visual Studio 2008 Add-in called Paste as Xml Types that makes generating types from an XML schema as easy as copying and pasting.  Unfortunately, the add-in will not work in Visual Studio 2010.  But look for a similar Add-in for Visual Studio 2010 on the Visual Studio Gallery in the future.

 

Step 6: Using the Automatic Help Page in WCF WebHttp Services

Writing the client code like we have been in this blog post so far has been easy because we wrote the TeamTask service and we know what the valid URIs for the service look like and what kind of response content we should receive.  But of course this isn’t always the case. 

If your are developing a client for a web service like TeamTask service and you didn’t implement the service, how are you supposed to know how to use the service?  Likewise, if you are developing a web service like the TeamTask service but are allowing other developers to implement clients for the service, how are you supposed to communicate how to use the service? 

The answer is the new automatic help page that is provided with WCF WebHttp Services in .NET 4.  The best way to get a sense of the automatic help page is to see it for yourself.

  1. Start without debugging (Ctrl+F5) to start the TeamTask service.

  2. When the automatic help page is enabled (it is enabled by default with the online project template), it can be found at the URI “/help” relative to your service’s base address.  So in the browser of your choice, navigate to http://localhost:8080/TeamTask/help.  In Internet Explorer, the help page will be displayed as shown below:

    OperationsHelpPageInBrowser 

    The automatic help page provides a list of the valid URIs, the HTTP methods that can be used with a given URI and a default description of the operation.  We’ll see how to set the descriptions to something more useful in the next step.

  3. Notice that the HTTP methods listed in the automatic help page happen to be hyperlinks.  Follow the GET hyperlink for the “Tasks” URI.  In Internet Explorer, the following page will be displayed:

    UriHelpPageInBrowser

    This page offers a lot of useful information about the given operation, including the URI, HTTP method, the request and response formats (XML and JSON), examples in both XML and JSON, and even the XML schema.  This page provides client developers with all of the information they need to be successful.

Step 7: Providing Descriptions for the TeamTask Operations

As we saw in step six, the main help page at the relative  “/help” URI provides a list of operations that the service exposes.  For each of theses operations, there is a description field, which is just the absolute URI for the operation by default.  Providing client developers with more useful descriptions is as easy as adding [Description] attributes to the operations of the service.

  1. Open the TeamTaskService.cs file in the code editor if it isn’t already open.

  2. Add “using System.ComponentModel;” to the code file.

  3. Add a [Description] attribute to the GetTasks() method like so:

        [Description("Returns the tasks that are owned by the team.")] 
        [WebGet(UriTemplate = "Tasks?skip={skip}&top={top}&owner={userName}")] 
        public List<Task> GetTasks(int skip, int top, string userName)

  4. Add a [Description] attribute to the GetUser() method like so:

        [Description("Returns the details of a user on the team.")] 
        [WebGet(UriTemplate = "Users/{userName}")] 
        public User GetUser(string userName)

  5. Start without debugging (Ctrl+F5) to start the TeamTask service.  In the browser of your choice, navigate to http://localhost:8080/TeamTask/help.  In Internet Explorer, the help page will be displayed as shown below:

    HelpPageWithDescriptionsInBrowser

    Notice that the description fields for the operations now reflect the values from the operation attributes.  Note:  You may need to stop the ASP.NET development web server and then restart the TeamTask service in order to see the changes to the help page.  To stop the development server, right-click on the notification icon in the notification area of the Windows Taskbar and select "Stop" from the context menu that appears.  

Next Steps: Updating State in WCF WebHttp Services

We’ve got our TeamTask service up and running and we’ve also created a client that can retrieve data from the TeamTask service.  However, with a service like the TeamTask service, static server state isn’t very interesting.  In part three of this blog post series, we’ll add a new operation to the TeamTask service that will allow clients to update tasks.

Randall Tombaugh
Developer, WCF WebHttp Services

Come on, admit it.  You’ve been thinking for months that it’s time to take the dive and learn WCF better, but something always gets in the way. 

Well, to make learning WCF easier than ever, we’re rolling out some new installments in the “Introduction to Window Communication Foundation” series of tutorials.

We’ve recently added three new articles, and more will be rolling out the next few weeks:

  • Hosting Windows Communication Foundation Services
  • Self Hosting Windows Communication Foundation Services
  • Creating and Maintaining Service and Data Contracts

    Each tutorial provides just enough background information to get you started and then walks you through building a sample application so you can get some hands-on experience.  And there are completed code samples in C# and VB that you can download.

    It should take less than an hour to go through a tutorial.  So why not schedule in some self-training time over the next few weeks.  Grab a cup of coffee, roll up your sleeves and learn WCF!

    And please email me at Kent.Brown@microsoft.com if you have ideas for other tutorials you’d like to see.

  • 0 Comments
    Filed under: ,

    This is part one of a twelve part series that introduces the features of WCF WebHttp Services in .NET 4.  In this post we will cover:

    • Downloading and using the new online project templates for WCF WebHttp Services
    • Learning the basics of a WCF WebHttp Service: [WebGet] and UriTemplates
    • Creating a simple web service that honors HTTP GET requests to developer-defined URIs

    The TeamTask Web Service

    Over the course of this blog post series, we will be building a web service called TeamTask that utilizes all of the new features available in WCF WebHttp Services.  The TeamTask service allows a team to track tasks that are assigned to members of the team.  It is a relatively simple service that introduces the concept of a user and the concept of a task.  A user has a name, a manager, and a list of assigned tasks.  A task has an id, a title, a status (in progress, blocked, completed, etc.), relevant dates and an owner.

    For our TeamTask service, we will be using an ADO.NET Entity Data Model(EDM) to model these concepts of a user and a task.  Here is a graphical view of the data model for the TeamTask service:

    EntityDataModel

    Downloading the TeamTask Code

    At the end of this blog post, you’ll find a link that will allow you to download the code for the current TeamTask Service as a compressed file.  After extracting, you’ll find that it contains “Before” and “After” versions of the TeamTask solution.  If you would like to follow along with the steps outlined in this post, download the code and open the "Before" solution in Visual Studio 2010.  If you aren’t sure about a step, refer to the “After” version of the TeamTask solution.

    Note:  If you try running the sample code and see a Visual Studio Project Sample Loading Error that begins with “Assembly could not be loaded and will be ignored…”, see here for troubleshooting.

    Getting Visual Studio 2010

    To follow along with this blog post series, you will need to have Microsoft Visual Studio 2010 and the full .NET 4 Framework installed on your machine.  (The client profile of the .NET 4 Framework is not sufficient.)  At the time of this posting, the Microsoft Visual Studio 2010 Ultimate Beta 2 is available for free download and there are numerous resources available regarding how to download and install, including this Channel 9 video.

     

    Step 1: Downloading the New Online Project Templates

    You won’t find any project templates for WCF WebHttp Services out-of-the-box with Visual Studio 2010.  However, using a new feature in Visual Studio 2010 called the Extension Manager, locating and downloading our new online templates is quick and easy. 

    1. In Visual Studio 2010, open the Extension Manager from the main menu bar using “Tools”—>”Extension Manager”.
    2. On the left-hand side of the Extension Manager, select the “Online Gallery” and then select “Templates”—>”WCF” from the tree-view control that appears.  This will list the relevant templates that are available online.  At the time of this writing, the following templates were available:

      ExtensionManager

    3. Click on the “Download” button for the WCF REST Service Template 40(CS) and follow the prompts.  Of course, if you are more comfortable with Visual Basic than C#, there is also a VB template. (Although this blog post series will be entirely in C#.)  Note: There are also templates for .NET 3.5, but using these templates will preclude the use of the new .NET 4 features that are the focus of this blog post series.

    Step 2: Creating the TeamTask.Service Project

    With the online template installed, you can now easily create a new WCF WebHttp Service.

    1. If you haven't already done so, download and open the “Before” solution of the code attached to this blog post.  You’ll find that the solution doesn’t have a project but includes a number of loose files within two solution folders: Database and Model.  We’ll move these into our new project after we create it.

    2. In the “Solution Explorer” window (Ctrl + W, S), right click on the TeamTask solution and select “Add”—>”New Project”. This will open the “New Project” dialog window.

    3. On the left-hand side of the “New Project” dialog window select “Installed Templates” and then select “Visual C#”—>”Web” from the tree-view control.  Choose the “WCF REST Service Application” project template.  Note: If you do not see the installed “WCF REST Service Application” project template, you might need to enable the loading of per-user extensions.  There will be a link below the template selection area if this is the case.  Follow this link to enable the loading of per-user extensions.  You may need to restart Visual Studio.

    4. For the project name enter “TeamTask.Service” and then click “Ok”.  This will create the TeamTask.Service project in the solution.

    Step 3: Exploring the Online Template Project

    Before we start implementing our service, we should briefly look at the items that were created in the TeamTask.Service project with the online template.  If you’re unsure about the code in these files, don’t worry.  We’ll explain the reason behind every custom attribute and configuration setting over the course of building the TeamTask service.

    • Service1.cs: This code file provides an empty implementation of a WCF WebHttp Service class.  The Service1 class includes web operations, which are methods decorated with [WebGet] and [WebInvoke] attributes to indicate the HTTP method and URIs that map to the method.

    • SampleItem.cs: This code file provides a default data type that is used by the Service1 class. The SampleItem class is deserialized/serialized as the body of request/response HTTP messages.

    • Web.config: This is a standard .NET configuration file created for web projects, although this one doesn’t have a lot of markup.  One of the new features in WCF WebHttp Services is the simplified configuration.

    • Global.asax: This is an ASP.NET application file that can be used in WCF WebHttp Services for registering simple ASP.NET Routes for your service(s).  Routing is a useful way of composing smaller, more manageable WCF WebHttp Services into a larger web application.  We’ll look at routes in more detail in part six of this blog series.  

    Helpful Tip: If you’ve used WCF 3.5 or the WCF REST Starter Kit to build non-SOAP HTTP services in the past, you may recall that hosting in IIS required a .svc file.  You may also notice that the online template for WCF WebHttp Services in .NET 4 doesn’t generate a .svc file.  This is because ASP.NET routing can now be used instead of .svc files when hosting in IIS. Routes are generally preferable because they give you more control over your URIs.

     

    Step 4: Adding the Database and Entity Framework Items

    The loose files within the “Before” solution attached to this blog post were created to save you the effort of having to create them yourself.  None of these items are essential to a WCF WebHttp Service so we didn’t want to cover their creation in depth, but we’ll be using them for our TeamTask web service.  Within the “Database” solution folder is an in-project database that has already been created for the TeamTask service.  Within the “Model” solution folder is an ADO.NET Entity Data Model (EDM) (TeamTask.edmx) and related classes. 

    If you are unfamiliar with the ADO.NET Entity Framework, it provides object/relational mapping to easily move data between a database and in-memory CLR instances.  The types that the ADO.NET Entity Framework populate with data from the backing data store are referred to as entity types.  For the TeamTask service, we have the Task and User entity types in the “Model” solution folder. 

    Actually the Task and User types are POCO entity types.  POCO entities types are a new feature in the ADO.NET Entity Framework for .NET 4.  With POCO entity types, a developer has complete control over the type definition and the attributes that are applied to the type and its members.  Later, we’ll see that this is important because it allows us to control the content of our HTTP responses.  You can see here for details about POCO entity types and how they can be created, but all you need to understand is that the POCO entity types “work” because they have properties with the same names as those used in the EDM (TeamTask.edmx). 

    The “Model” solution folder also contains a hand-coded TeamTaskObjectContext class (TeamTaskObjectContext.cs) that derives from the ObjectContext base class in the Entity Framework.  The TeamTaskObjectContext is the primary means by which we’ll interact with the database as it exposes collections of the User and Task POCO entity types.

    1. In the “Solution Explorer” window (Ctrl + W, S), right click on the TeamTask.Service project and select “Add”—>”New Folder”.  Name the new folder “Model”.

    2. Copy all of the files from the “Model” solution folder into the “Model” folder of the TeamTask.Service.

    3. Copy the TeamTask.mdf and TeamTask_log.ldf files from the “Database” solution folder into the “App_Data” folder of the TeamTask.Service.

    4. In order for the TeamTask code  to get task and user data from the database, we need to add a database connection string to the Web.config file of the TeamTask.Service project.  Open the App.Config file in the “Database” solution folder and copy the full <connectionStrings> element into the <configuration> element of the Web.config.  Note:  Only SQL Express supports the "User Instance = true" setting in the connection string.  If you are not using SQL Express you will need to remove this setting from the connection string.

    5. We no longer need the loose files, so right click on the “Solution Items” folder in the  “Solution Explorer” window and select “Remove” from the context menu.

    6. Because we want to use our POCO entity types, we need to disable the default entity generation that the ADO.NET Entity Framework provides.  In the “Solution Explorer” window (Ctrl + W, S), right click on the TeamTask.edmx file and select “Properties” from the context menu.  In the “Properties” window, you’ll find that the “Custom Tool” entry has a value of “EntityModelCodeGenerator”.  Clear this value and leave it blank as shown below.  This will disable the default entity generation.

    DisablingDefaultEntityGeneration

    Step 5: Setting the Base Address of our Service

    When creating a new web project in Visual Studio the project will be configured by default to use a randomly assigned port with the development server.  To reduce confusion and allow you to copy and paste URIs from these blog posts without editing the port, we’ll configure the debug development server to use a specific port.

    We’ll also configure the virtual path for the TeamTask service and set the route path for our service to an empty string.  The route path is part of the new routes feature introduced with WCF WebHttp Services in .NET 4.  We’ll discuss routes in more detail in part six of this blog post series.  For now, the important thing to understand is that the base address of the TeamTask service is constructed from the host name and port, the virtual path and the route path (which will effectively be nothing as an empty string).

    1. In the “Solution Explorer” window, right click on the TeamTask.Service project and select “Properties” from the context menu.

    2. In the TeamTask.Service properties editor, select the “Web” tab.

    3. Under the “Servers” section, click on the “Specific Port” radio button and enter “8080” as the port number.  Also set the virtual path to “/TeamTask”.

    4. Open the Global.asax file in the code editor.

    5. In the RegisterRoutes() method, replace the route path “Service1” with an empty string like so:

          RouteTable.Routes.Add(new ServiceRoute("", new WebServiceHostFactory(),
                                            typeof(Service1)));

      Now when we run the TeamTask service from within Visual Studio, all of the service operations will be exposed at URIs relative to the base address:

          http://localhost:8080/TeamTask/

    Step 6: Getting a List of Tasks from the TeamTaskService

    We’re now ready to create the TeamTaskService.  We’ll implement a simple service operation that returns all of the tasks from the database.

    1. Rename the Service1.cs file to “TeamTaskService.cs” by right-clicking on it in the “Solution Explorer” window and selecting “Rename” from the context menu.  A dialog box should appear asking if you want to rename all references in the project to the code element “Service1”.  Select “Yes”.

    2. Open the newly renamed TeamTaskService.cs file in the code editor.

    3. Since we only want a single service operation for now, delete all of the operations in the TeamTaskService class except for the GetCollection() operation, which you should rename to GetTasks().

    4. Change the UriTemplate value on the GetTasks() operation from an empty string to “Tasks”.  This specifies that an HTTP GET request with a URI of “http://localhost:8080/TeamTask/Tasks” will be handled by the GetTasks() operation.  This is a simple UriTemplate that specifies just a single URI, but as we’ll see later, UriTemplates can be much more expressive and can represent entire sets of URIs.

    5. Change the return type of the GetTasks() operation to List<Task>.  In order to do this, you’ll need to add “using TeamTask.Model;” to the code file.

    6. Implement the GetTasks() operation such that it uses the TeamTaskObjectContext to get all of the tasks from the database.  The TeamTaskObjectContext is the ADO.NET ObjectContext that we hand-coded to work with our POCO entity types.  It allows us to query the database for the tasks using LINQ: 

          [WebGet(UriTemplate = "Tasks")] 
          public List<Task> GetTasks() 
          {  
              using (TeamTaskObjectContext objectContext = 
                        new TeamTaskObjectContext()) 
              { 
                  return objectContext.Tasks.OrderBy(task => task.Id).ToList();
              } 
          }

    7. Start without debugging (Ctrl+F5) and use the browser of your choice to navigate to http://localhost:8080/TeamTask/Tasks.  In Internet Explorer, the list of tasks will be displayed as shown below:

      ListOfTasksInBrowser_sansUserNames 

    Helpful Tip: If you’ve used WCF 3.5 or the WCF REST Starter Kit to build non-SOAP HTTP services in the past, you may recall that operations required both a [WebGet] or [WebInvoke] attribute and an [OperationContract] attribute. You may also notice that we are not using [OperationContract] attributes on the GetTasks() operation.  This is because in WCF WebHttp Service for .NET 4, the [OperationContract] attribute is now optional.

     

    Step 7: Getting a User and His or Her Tasks

    Now that we have an operation for retrieving a list of tasks from our TeamTask service, let’s create a second operation for retrieving users.  With this new operation we’ll use a UriTemplate that contains a variable so that individual users can be retrieved by supplying a username in the URI.

    1. Open the TeamTaskService.cs file in the code editor if it isn’t already open.

    2. Add a public method called GetUser() that takes a username parameter of type string and returns a User type like so:

          public User GetUser(string userName)

    3. To make the GetUser() method an explicit service operation, add a [WebGet] attribute with a UriTemplate value of “Users/{userName}” like so:

          [WebGet(UriTemplate = "Users/{userName}")] 
          public User GetUser(string userName)

      Notice that the UriTemplate has the name “userName” surrounded by curly braces.  These curly braces indicate that “userName” is a variable and not a literal like the “Users” portion of the UriTemplate.  The presence of this “userName” variable means that this UriTemplate specifies a set of URIs instead of just a single URI.  In this case, the UriTemplate specifies URIs that have a path of “Users/” followed by one additional path segment.  For example, URIs (relative to the service base address) such as “Users/annc” or “Users/maryt” would match this UriTemplate, but URIs such as  “Users/”, “Tasks/annc”, or “Users/maryt/reports” would not.

      Also notice that the GetUser() method has a parameter with the name “userName”.  It is not a coincidence that the UriTemplate variable and the method parameter share the same name.  By using the same name, the WCF WebHttp Services infrastructure knows to map the UriTemplate variable value from the URI of the request to the method parameter when the operation is invoked.  For example, if the request URI were “Users/annc”, the GetUser() method would be invoked with the value of “annc”.

    4. Implement the GetUser() operation using the TeamTaskObjectContext, similar to what was done for the GetTasks() operation:

          using (TeamTaskObjectContext objectContext =
                    new TeamTaskObjectContext()) 
          { 
                // The 'Include' method is an ADO.NET Entity Framework feature that
                //   allows us to load the user and his/her tasks in a single query! 
               
      var user = objectContext.Users.Include("Tasks")  
                                   .FirstOrDefault(u => u.UserName ==  userName); 
                return user; 
          }

    5. Start without debugging and navigate to http://localhost:8080/TeamTask/Users/user1.  In Internet Explorer, the list of tasks will be displayed as shown below:User1InBrowser

    Helpful Tip: For more details about how UriTemplates work, see here.

     

    Step 8: Filtering the List of Tasks with Query String Parameters

    With a limited number of tasks in the database, having the TeamTask service return every task with each request isn’t a problem.  But of course, for a real world web service with lots of tasks this would be impractical.  We need a way to filter the list of tasks on the server, and we can do this by adding query string parameters to the UriTemplate on the GetTasks() operation. 

    We’ll add “skip” and “top” query string parameters to provide a paging mechanism; given an ordered list of tasks, the “skip” parameter will indicate not to return the first N tasks in the list and the “top” parameter will indicate the maximum number of tasks to return.

    We’ll also add an “owner” query string parameter via which a username can be supplied.  When the “owner” is specified, only tasks for the given user will be included in the list of tasks returned.

    1. Open the TeamTaskService.cs file in the code editor if it isn’t already open.

    2. The UriTemplate value for the GetTasks() operation is already “Tasks”. Add the query string parameters “skip”, “top” and “owner”  along with UriTemplate variables.  Also add parameters to the method with names that match the UriTemplate variables like so:

          [WebGet(UriTemplate = "Tasks?skip={skip}&top={top}&owner={userName}")] 
          public List<Task> GetTasks(int skip, int top, string userName)

      Notice that the skip and top method parameters are of type int and not simply strings.  With query string variables, the WCF WebHttp Services infrastructure can provide conversion from the string value to other common types.

    3. Query string parameters are not required, so it is possible that our GetTasks() operation will handle request URIs that don’t include “skip”, “top” or “owner” query string values.  Add some code to make sure we use reasonable “skip” and “top” values like so:

          // Set reasonable defaults for the query string parameters
          skip = (skip >= 0) ? skip : 0
          top = (top > 0) ? top : 25;

      Note: If a query string value isn’t present in the request URI, the method parameter will be the default value of its given type.

    4. We also need to check for a null userName value, but this can be done while building the query, because we only want to include a LINQ Where clause if there is a userName value:

          // Include the where clause only if a userName was provided 
          IQueryable<Task> taskQuery = (string.IsNullOrWhiteSpace(userName)) ? 
          objectContext.Tasks.AsQueryable<Task>() : 
          objectContext.Tasks.Where(task => task.OwnerUserName == userName);

    5. Lastly, add the LINQ Skip and Take clauses to the query so that the final implementation of the GetTasks() operation looks like:

          [WebGet(UriTemplate = "Tasks?skip={skip}&top={top}&owner={userName}")] 
          public List<Task> GetTasks(int skip, int top, string userName) 
          { 
              // Set reasonable defaults for the query string parameters 
             
      skip = (skip >= 0) ? skip : 0
              top = (top > 0) ? top : 25

              using (TeamTaskObjectContext objectContext =
                        new TeamTaskObjectContext()) 
              { 
                  // Include the where clause only if a userName was provided  
                 
      var taskQuery = (string.IsNullOrWhiteSpace(userName)) ? 
                      objectContext.Tasks : 
                      objectContext.Tasks.Where(
                          task => task.OwnerUserName == userName); 

                  return taskQuery.OrderBy(task => task.Id)
                                          .Skip(skip).Take(top).ToList(); 
              } 
          }

    6. Start without debugging and navigate to: http://localhost:8080/TeamTask/Tasks?skip=1&top=2.  The list of tasks will be filtered to only include tasks 2 & 3 as shown below:

      FilteredTaskListInBrowser_sansUserNames

       

      Notice that the “owner” query string parameter wasn’t provided, so the tasks were not filtered by userName. 

    Next Steps: Clients and the Automatic Help Page in WCF WebHttp Services

    We’ve got our TeamTask service up and running, and we can use the browser to retrieve tasks and users.  But web services are all about programmatic access, so in the part two of this series we’ll create a client to consume our TeamTask service.  We’ll also look at the automatic help page feature in WCF WebHttp Services that makes it easier to build such clients.

    Randall Tombaugh
    Developer, WCF WebHttp Services

    Over the next six weeks we are going to be releasing a series of blog posts that will focus on the new features in .NET 4 around WCF WebHttp Services

    WCF WebHttp Services is the flavor of WCF that is most appropriate for developers who need complete control over the URI, format, and protocol when building non-SOAP HTTP services—services that may or may not subscribe to RESTful architectural constraints.  For the other technologies in the WCF services eco-system and the developer scenarios they best support, see here

    For those of you who have used WCF 3.5 or the WCF REST Starter Kit to build non-SOAP services, you’ll find that WCF WebHttp Services is the vehicle that brings these technologies forward to .NET 4.  You’ll find the familiar programming model that uses [WebGet] and [WebInvoke] attributes to map an HTTP request to a WCF operation.  But you’ll also find a number of new features in WCF WebHttp Services that make it even easier to build operation-centric or RESTful web services over HTTP.

    For those of you who are new to WCF or who have only used WCF for building SOAP-based services in the past, you’ll find that this series of blog posts will bring you up to speed quickly on everything you need to know to start building non-SOAP HTTP services in which you have complete control over the URI, format and protocol.      

    To give you a sense of the features we will be covering in this series on WCF WebHttp Services in .NET 4, here is an index of the upcoming blog posts (we’ll update these links as the posts go public):

    1. Getting Started with WCF WebHttp Services in .NET 4
    2. Clients and the Automatic Help Page in WCF WebHttp Services
    3. Updating State in WCF WebHttp Services
    4. Automatic and Explicit Format Selection in WCF WebHttp Services
    5. Error Handling in WCF WebHttp Services with WebFaultException
    6. Using Routes to Compose WCF WebHttp Services
    7. Integrating ASP.NET Output Caching with WCF WebHttp Services
    8. Returning Custom Formats from WCF WebHttp Services
    9. Creating Views in WCF WebHttp Services with T4
    10. Conditional GET and Etag Support in WCF WebHttp Services
    11. Optimistic Concurrency Support in WCF WebHttp Services
    12. Guidance on Link Generation in WCF WebHttp Services  

    We’re really excited about the new features introduced with WCF WebHttp Services in .NET 4 and we look forward to hearing your feedback in the comments to the blog posts.  Hope you find the upcoming blog post series both interesting and informative.

    Randall Tombaugh
    Developer, WCF WebHttp Services

    In .NET 4.0, we have introduced a framework for correlation.  What do I mean by correlation?  I’m glad you asked.  In our vocabulary, a “correlation” is actually one of two things:

    1. A way of grouping messages together.  A classic example of this is sessions in WCF, or even more simply the relationship between a request message and its reply. 
    2. A way of mapping a piece of data to a service instance.  Let’s use sessions as an example here also, because it makes sense that I’d want all messages in a particular session (SessionId = 123) to go to the same instance (InstanceId = 456).  In this case, we’ve created an implicit mapping between SessionId = 123 and InstanceId = 456.

    As you can see, these patterns are related, hence why we call them both “correlations”.  But sessions are inherently short-lived, tied to the lifetime of the channel.  What happens if my service is long-running and the client connections aren’t?  The world of workflow services reinforces the need for a broader correlation framework, which is why we’ve invested in this area in .NET 4.0. 

    There are two operations that can be performed on a correlation: it can be initialized, or it can be followed.  This terminology is not new; in fact, Biztalk has had correlation for many releases.  But what does “initializing” a correlation mean?  Another great question.  Well, it is simply creating this mapping between the data and the instance.

    In addition, there are many types of correlation available in .NET 4.0, but let’s focus on this category of associating data with an instance.  When that data comes from within the message itself (e.g. as a message header or somewhere in the body of the message), we call that content-based correlation

    Ok, too much theory and not enough application; let’s look at how this manifests in WF 4.0.  With every message sent or received from your workflow, you’ve got an opportunity to create an association between a piece of data in that message and the workflow instance.  That is, every messaging activity (Receive, SendReply, Send, & ReceiveReply) has a collection of CorrelationInitializers which let you create these associations.  Here’s what the dialog looks like in Visual Studio 2010:

    CorrInitializers

    As you can see, it’s been populated with a Key and a Query.  The Key is just an identifier used to differentiate queries, e.g. DocId = 123 should be different than CustomerId = 123.  The Query part is how we retrieve the data from the message; in this case, it’s an XPath expression that points within the body of the ReceiveDocument request message to the Document.Id value.  Some of the resulting correlation information is pushed into a CorrelationHandle variable (the DocIdCorrelation), which will be used by later activities to follow this correlation.

    Now, you might be wondering: if I already have the Document.Id value in a workflow variable, why do I need this XPath expression in order to initialize my correlation?  That’s a great point.  In fact, we wrote another activity just for this purpose: the InitializeCorrelation activity.  As you would expect, the dialog looks very similar to what we just saw (Note: this dialog is different than what is present in Visual Studio 2010 Beta2):

    InitializeCorrelation

    This activity is particularly useful in scenarios where the data to initialize the correlation comes from a database, or if you need to create a composite correlation from multiple pieces of data like a customer’s first and last name concatenated together.  For all of you Biztalk experts, this means no more “dummy send” pattern!  Hooray!

    Ok, you’ve initialized a correlation … now what?  Regardless of how you’ve initialized it, a correlation is only useful if it is followed.  A Receive activity follows a correlation by correlating on the same piece of data (in this case, a DocId) and specifying the same CorrelationHandle.  Imagine that the document approval process includes some opportunity to update the document before the official approval is given.  An UpdateDocument request message is sent, which contains the DocumentId value in it.  Here we specify the XPath expression to point to that particular piece of data in our incoming message.  We also set the CorrelatesWith property to the same CorrelationHandle we specified previously; this ensures that when the Receive activity starts executing and goes idle (sets up its bookmark), the WorkflowServiceHost knows what correlation this workflow is waiting on and can resume the correct instance when the message with the corresponding Document Id comes in. 

    CorrelatesOn  

    And now you can consider yourself a content-based correlation expert!  No longer are you restricted to using a context-based binding for communicating with instances of your workflow services!  Now that’s freedom.  Give it a shot and let us know what you think!

    More Posts Next page »
     
    Page view tracker