December 2008 - Posts

My good friend Pablo Galiano has released a pretty cool plug-in for Visual Studio 2008, Sticky Notes. In a few words, this extension allows you to attach sticky notes to your code, which can be personal notes (Only visible to you) or team notes (visible to the entire team).

Give it a try!!.

Posted by cibrax | 1 comment(s)
Filed under:

I just created some issues in codeplex for the features I discussed last week in these two posts:

Add support for switching the content type dynamically

http://www.codeplex.com/aspnet/WorkItem/View.aspx?WorkItemId=2846

Related Post: http://weblogs.asp.net/cibrax/archive/2008/12/05/dynamic-content-type-a-nice-to-have-feature-for-the-rest-starter-kit.aspx

Add support for conditional get in WebCache behavior

http://www.codeplex.com/aspnet/WorkItem/View.aspx?WorkItemId=2847

Related Post: http://weblogs.asp.net/cibrax/archive/2008/12/08/adding-conditional-get-support-to-the-wcf-rest-starter-kit.aspx

Feel free to vote any of them :)

Posted by cibrax | 2 comment(s)
Filed under: ,

Many of the WCF services that we build today rely on the WCF context (OperationContext and WebOperationContext) for performing different things, specially REST services where the context is necessary for settings and getting Http status codes or headers. The fact that the WCF context does not expose interfaces or base classes complicates the unit testing a lot. In this sense, I like the approach taken by the ASP.NET MVC team, all the classes exposed by the HttpContext as properties are base classes, so they can be easily mocked.
In order to test a WCF service with what we have today, we have to either test the service as a black box (integration tests, which requires a lot of plumbing code to setup all the WCF infrastructure for the test, channels, host, client, etc) or create some wrappers to encapsulate the WCF context behavior, as I mentioned in this post "Unit tests for WCF services"

Today I will discuss both approaches more in detail with some sample code. The service implementation I will use here is a simple service that given a category returns a feed with products associated to that specific category.

[ServiceContract]

public interface IProductCatalog

{

    [WebGet(UriTemplate = "?category={category}")]

    [OperationContract]

    Atom10FeedFormatter GetProducts(string category);

}

 

public Atom10FeedFormatter GetProducts(string category)

{

    var items = new List<SyndicationItem>();

    foreach(var product in repository.GetProducts(category))

    {

        items.Add(new SyndicationItem()

        {

            Id = String.Format(CultureInfo.InvariantCulture, "http://products/{0}", product.Id),

            Title = new TextSyndicationContent(product.Name),

            LastUpdatedTime = new DateTime(2008, 7, 1, 0, 0, 0, DateTimeKind.Utc),

            Authors =

            {

                new SyndicationPerson()

                {

                    Name = "cibrax"

                }

            },

            Content = new TextSyndicationContent(string.Format("Category Id {0} - Price {1}",

                product.Category, product.UnitPrice))

        });

    }

 

    var feed = new SyndicationFeed()

    {

        Id = "http://Products",

        Title = new TextSyndicationContent("Product catalog"),

        Items = items

    };

 

    MyWebContext.Current.OutgoingResponse.ContentType = "application/atom+xml";

    return feed.GetAtom10Formatter();

}

As you can see, this is a regular WCF service with the following characteristics,

1. The products are queried from a product repository, a class that implements IProductRepository so it can be mocked.

public interface IProductRepository

{

    IQueryable<Product> GetProducts(string category);

}

2. It is not using the WCF context (WebOperationContext), it is using MyWebContext instead. This is a custom class I created for wrapping the WCF context behavior. As we will see later, this class will help us to mock the WCF context in the unit tests.

public class MyWebContext : IDisposable

{

    private const string TlsName = "webContext";

 

    public MyWebContext(IWebOperationContext context)

    {

        var tls = Thread.GetNamedDataSlot(TlsName);

        Thread.SetData(tls, context);

    }

 

    public static IWebOperationContext Current

    {

        get

        {

            var tls = Thread.GetNamedDataSlot(TlsName);

            var context = Thread.GetData(tls);

 

            if (context != null)

            {

                return (IWebOperationContext)context;

            }

            else

            {

                return new WebOperationContextWrapper(WebOperationContext.Current);

            }

        }

    }

 

    public void Dispose()

    {

        var tls = Thread.GetNamedDataSlot(TlsName);

        Thread.SetData(tls, null);

    }

}

The Current property of this class first tries to get an IWebOperationContext from the thread local storage (That could be set by the unit tests), and if none is available, it returns a wrapper to the original WCF context.

Integration Tests

Integration tests focus more on testing the service as a whole, checking not only the service behavior but also extensions in the WCF channel stack and any other external dependency referenced by the service itself. Integration tests are preferred over unit tests for certain scenarios, if I want to test for instance how a service behaves with an built-in extension for "conditional gets" in the WCF channel stack, that can be easily done with an integration test.

Integration tests usually have the following structure:

1. Some code to setup the WCF host, usually located at the beginning of the test or as part of the test fixture initializer.

2. The service invocation itself using a previously initialized web http request or an specific WCF channel.

[TestMethod]

public void ShouldGetProductsFeed()

{

    ProductCatalog catalog = new ProductCatalog(

        new InMemoryProductRepository(

            new List<Product>{

            new Product { Id = "1", Category = "foo", Name = "Foo1", UnitPrice = 1 },

            new Product { Id = "2", Category = "bar", Name = "bar2", UnitPrice = 2 }

        }));

 

    WebServiceHost host = new WebServiceHost(catalog, new Uri("http://localhost:7777/Products"));

 

    IEnumerable<SyndicationItem> items;

 

    try

    {

        host.Open();

 

        items = Consume(new Uri("http://localhost:7777/Products?category=foo"));

    }

    finally

    {

        if (host.State == System.ServiceModel.CommunicationState.Opened)

            host.Close();

        else

            host.Abort();

    }

 

    Assert.AreEqual(1, items.Count());

    Assert.IsTrue(items.Any(i => i.Id == "http://products/1" && i.Title.Text == "Foo1"));

}

"Consume" in the code above is a helper method that internally uses WebHttpRequest for sending a http get to the WCF service.

private IEnumerable<SyndicationItem> Consume(Uri feedUri)

{

    WebRequest request = CreateWebRequest(feedUri);

    XmlReaderSettings settings = new XmlReaderSettings { CloseInput = true };

    using (XmlReader reader = XmlReader.Create(request.GetResponse().GetResponseStream(), settings))

    {

        SyndicationFeed feed = SyndicationFeed.Load(reader);

        return feed.Items;

    }

}

 

private WebRequest CreateWebRequest(Uri feedUri)

{

    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(feedUri);

    webRequest.ContentType = "application/rss+xml";

 

    webRequest.Timeout = 60 * 1000;

 

    webRequest.Method = "GET";

 

    return webRequest;

}

Unit Tests

Unit tests focus on the service implementation only, leaving out most of the external dependencies and any additional processing made by the WCF channel stack. The IWebOperationContext interface I mentioned before will help us to mock many of the properties provided by the WCF context. This is a good thing for getting/setting expectations that we want to verify in the test itself. This interface and the wrappers are available as part of the project Netfx, http://code.google.com/p/netfx/

[TestClass]

public class UnitTests

{

    [TestMethod]

    public void ShouldGetProductsFeed()

    {

        ProductCatalog catalog = new ProductCatalog(

            new InMemoryProductRepository(

                new List<Product>{

                new Product { Id = "1", Category = "foo", Name = "Foo1", UnitPrice = 1 },

                new Product { Id = "2", Category = "bar", Name = "bar2", UnitPrice = 2 }

            }));

 

        Mock<IWebOperationContext> mockContext = new Mock<IWebOperationContext> { DefaultValue = DefaultValue.Mock };

        IEnumerable<SyndicationItem> items;

 

        using (new MyWebContext(mockContext.Object))

        {

            var formatter = catalog.GetProducts("foo");

            items = formatter.Feed.Items;

        }

 

        mockContext.VerifySet(c => c.OutgoingResponse.ContentType, "application/atom+xml");

 

        Assert.AreEqual(1, items.Count());

        Assert.IsTrue(items.Any(i => i.Id == "http://products/1" && i.Title.Text == "Foo1"));

    }

}

What's deserve some attention in the unit test above is the code for creating the mocked WebContext and the code required for verifying the expectations.

using (new MyWebContext(mockContext.Object))

{

    var formatter = catalog.GetProducts("foo");

    items = formatter.Feed.Items;

}

That will insert the mockContext object in the Thread Local Storage so it can be accessed later in the service implementation by MyWebContext.Current (The "using" clause here makes an explicit call to IDispose as well, so the mockContext is removed from that storage after the operation is called).  

The test also verifies that the ContentType header was set with the correct value in the operation,

mockContext.VerifySet(c => c.OutgoingResponse.ContentType, "application/atom+xml");

Complete code available to download from here

Posted by cibrax | 7 comment(s)
Filed under: ,

Some weeks ago, I discussed how important "Conditional Get" can be for some scenarios, specially when we want to make a better use of the network traffic.

The WCF REST Starter kit introduced a new extension "WebCache", implemented as an operation behavior, to automatically add caching support to any "Get" operation in a service contract. Using this new feature is a simple as annotating an existing service operation with a "WebCache" attribute, as it is shown below:

[WebCache(CacheProfileName = "CacheFor1Min")]

[WebGet(UriTemplate = "customer/{id}")]

[OperationContract]

public Customer GetCustomer(string id)

The underline implementation of this new behavior relies on the existing ASP.NET web cache. For more details about this new feature, Jesus Rodriguez has written a very good summary here. I also mentioned how to enable Sql dependencies with this feature in this post.

Unfortunately, this implementation does not support Conditional Get out of the box, which means that the client always receive a complete response, no matter if the response was served directly from the cache or not. For instance, if one operation returns a feed with one hundred items, and the client application does not have a way to specify when was the last time it got access to that feed, it will have to download the complete feed all the times.

Although the ASP.NET does provide support for conditional gets through the public methods "SetLastModified" (for date times) and "SetETag" (for entity representations), the web cache behavior does not make use of them.

If we just modified the Web cache behavior to include the following line at the end, the additional LastModified header will be included in the response

if (!string.IsNullOrEmpty(cacheProfile.VaryByCustom))

{

    HttpContext.Current.Response.Cache.SetVaryByCustom(cacheProfile.VaryByCustom);

}

 

HttpContext.Current.Response.Cache.SetLastModified(DateTime.Now); //Line Added for Conditional Get support

It is a simple as that. After enabling some trace to the service, we will see

Server ASP.NET Development Server/9.0.0.0
Date Mon, 08 Dec 2008 15:41:54 GMT
X-AspNet-Version 2.0.50727
Cache-Control public, max-age=44
Expires Mon, 08 Dec 2008 15:42:42 GMT
Last-Modified Mon, 08 Dec 2008 15:41:43 GMT
Vary *
Content-Type application/xml; charset=utf-8
Content-Length 195
Connection Close

However, setting DateTime.Now for every operation is not efficient for every scenario because it will be tied to the item lifetime in the cache. For instance, if our service operation is returning a feed, we might want to use the feed's last update time.

It will be much better if have a configurable way to set up this date for every entry in the cache. For that reason, I came up with a solution based on a simple strategy pattern through a pluggeable interface "ILastModified",

public interface ILastModified

{

    DateTime? GetLastModifiedForOutput(object[] outputs, object returnValue);

}

The method "GetLastModifiedForOutput" basically determines the "LastModified" date for the given outputs parameters and return value of the service operation. Given this simple interface, we could have default implementations for feeds or the current date time.

public class SyndicationLastModified : ILastModified

{

    public DateTime? GetLastModifiedForOutput(object[] outputs, object returnValue)

    {

        if(returnValue is SyndicationFeedFormatter)

        {

            var formatter = (SyndicationFeedFormatter)returnValue;

            return formatter.Feed.LastUpdatedTime.UtcDateTime;

        }

 

        return null;

 

    }

}

 

public class CurrentDateLastModified : ILastModified

{

    public DateTime? GetLastModifiedForOutput(object[] outputs, object returnValue)

    {

        return DateTime.Now;

    }

}

And finally configure these classes as part of the WebCache attribute.

[WebCache(CacheProfileName = "CacheFor1Min", LastModifiedType=typeof(CurrentDateLastModified))]

[WebGet(UriTemplate = "customer/{id}")]

[OperationContract]

public Customer GetCustomer(string id)

{

    if (id == "foo")

        return new Customer { Id = id, FullName = "John Foo" };

    else if (id == "bar")

        return new Customer { Id = id, FullName = "John Bar" };

 

    return new Customer { Id = id, FullName = "Unknow" };

}

If no LastModifiedType is specified as argument for the "WebCache" attribute, the default behavior is used,  resulting in a complete response message served to the client all the times. Otherwise, the server will evaluate the "If-Modified-Since" header first to know if the valid response for that request should be "serving all the content" vs returning a "Http 304 - Not Modified" status code.

The sample code is available here

Posted by cibrax | 1 comment(s)
Filed under: ,

One of the missing features in the WCF web model is the ability to have a single operation definition and switch the content type for the response according to some runtime setting (Which could be the Accept or Content-Type request header for instance).

Today, I came across this post "WCF REST Services: Setting the response format based on request's expected type" that shows exactly that. The solution is very clean and based on some extensions to the existing WebHttpBehavior and WebHttpHost classes.

Kyle Beyer took a similar approach to switch the context type of the request messages as well. His implementation was layered on top of existing classes in the REST Starter kit, and it can find here.

Both implementations inspect the "Content-Type" header of the request messages to determine the WCF formatter to use. What it would very nice to have in the Starter kit is the possibility to switch the content type dynamically not only by the "Content-Type" header, but also by some other settings like UriTemplates or QueryString arguments.

It wouldn't be nice to have an attribute with the following syntax:

[WebHelp(Comment = "Returns the items in the collection in the format specified by the Accept header, along with URI links to each item.")]

[WebGet(UriTemplate = CollectionServiceBase<TItem>.ItemsUriTemplate)]

[OperationContract]

[DynamicContentType(Direction=DynamicDirection.Both, Resolution=DynamicResolution.QueryString, QueryStringArgument="content")]

ItemInfoList<TItem> GetItems();

Where Direction and Resolution could support some of these values,

public enum Direction

{

    RequestOnly,

    ResponseOnly,

    Both

}

 

public enum Resolution

{

    ContentTypeHeader,

    UriTemplate,

    QueryString

}

 

With that attribute in place, it will not be necessary anymore to redefine the same operation many times just to change the content type. For instance, the following service contract can be simplified with the use of this new attribute,

[OperationContract]

ItemInfoList<TItem> GetItemsInXml();

 

[WebGet(UriTemplate = CollectionServiceBase<TItem>.JsonItemsTemplate, ResponseFormat = WebMessageFormat.Json)]

[OperationContract]

ItemInfoList<TItem> GetItemsInJson();

I will see if I have some time to implement a prototype using that syntax for the DynamicContentType attribute.

In the meantime, I think it would be great to have support out of the box for a feature like this in the WCF REST Starter kit, what's your opinion ?

Posted by cibrax | 3 comment(s)
Filed under: ,

It's pretty interesting to see how useful workflow services can be sometimes, specially for business applications that require orchestrating several back-end services calls.

As one of the programmers involved in the development of the WS-I Sample application for .NET, an application that demonstrates  how to build interoperable web services in .NET using the WS-I Basic Profile 1.0 (Each vendor in the WS-I working group basically implemented the same application using its development platform, more information can be found here), it was a surprise to me to find another implementation as part of the WCF SDK, made this time all declaratively through WCF workflow services and just a few activities. To give you an idea, the original version of the same application took us some days and hundred lines of code just to have something complete and working.

The SDK version is available under the folder, [SDK Folder]\WCF_WF_CardSpace_Samples\WCF\Scenario\WorkflowServices\Conversations

Workflow services, initially introduced in WCF 3.5 with the Receive and Send activities, will now take a very important role as part of the Oslo vision. We will have the opportunity to write pure declarative long-running services with XAML, including all the WCF contracts for receiving/sending messages. This will make possible to store the complete workflow representation in the Oslo repository, and take later full control of this workflow's instances from Dublin, the new hosting environment for long-running services. David Chappell has written an excellent article about WF 4.0, Dublin the oslo vision, you can find it here, this a must read for developers interested in this area.

Posted by cibrax | 1 comment(s)
Filed under: ,
More Posts