WCF service contract design

A good place to start looking at service contract design is the standards document published by iDesign (found here). This article contains some additional thoughts to add to these standards.

These ideas have come about because of a desire to achieve service designs that have minimum impact on configuration for both the client and the server. The intention is to design service contracts that will live happily with the default limits defined in WCF. The most common reasons for tweaking the WCF default configuration values are that services are returning data that is too large, or has too many objects in the graph for serialization.

Here are some things to think of when designing service contracts:

Prefer chatty service interfaces

Chatty services tend to be ones that return simplified information and use more fine-grained operations. Chunky services tend to return complex hierarchies of information and use coarse operations. In other words, the difference is that chatty services require more calls than chunky services to return the same information but allow more flexibility to return only the information that is actually required.

Traditional services are typically chunky services. There are still a lot of people who believe that this is the only correct service design. This is probably for two reasons. Firstly, ease of use for referring to related data and secondly because connections are expensive.

As your service becomes chunkier, you are more likely to be returning information that is not relevant to the client. Returning redundant data is more expensive than additional connections. For example, if a service that returns information about a house, the design the House data contract may contain a collection of byte arrays that hold all the images related to the house. When looking at the service design, you would expect that all the related images would be returned when a house is retrieved.

What if there are a large number of images or the sizes of the images is large? WCF size limits may be hit in these scenarios. Each time a house object is requested, the service design also makes an assumption that the client will always want the images. Other than the additional overhead of redundant data being processed and sent over the wire, the apparent performance of the service decreases and users get cranky.

The alternative is to break up data contracts so that they only describe a singular entity rather than the entity and other entities that are related to it. Where it can be determined that a smaller set of information of the entity is very regularly requested, then this 'summary' information can be split out into another data contract with related operations on the service. For example, if 85% of house retrievals are interested in the address, not the other 15 properties on the House data contract, then you can create a HouseSummary data contract for greater efficiency. These metrics are normally determined through business analysis.

Only return array based types from an operation. Never return an array of items as a property of a data contract.

If a data contract contains a property that returns an array based type, the service design doesn't constrain how many items will be returned. Applications don't often have business rules that say you can only have a maximum of [insert number here] related records. These relationships are normally defined as a foreign key relationship in a relational database without business rules or constraints that limit the relationships. Taking the house example, the service design doesn't define the maximum number of house images can be stored for a house and business rules are most likely not going to address this either. The result is that it would be easy to have a record that has too many other related records such that we run into the default WCF size limits.

The exception to this rule is where there are clear business rules and/or data store constraints that define how much data is in the array. For example, an array of bytes that define a SHA1 hash value. These are always going to be 20 bytes. This is both a rule and a known maximum size limit. Knowing this helps to determine whether the data contract will break the default WCF size limits.  

Always support paging for operations that return array types

This basically has the same issue as returning array based types as properties of data contracts. Without paging parameters, the design of the service operation doesn't define how many items are returned. The same size limitations come into affect and the service call may crash because too much data is being passed around.

There is also a performance implication on this one as well. It is much quicker to return 10 items than it is to return 100 (assuming 100 items would fit within the size restrictions). Client UX is all about the experience. A slow UI is bad UX. The service design can make some safe assumptions that if 10 records are returned from an operation, the user will need to review those items before moving to the next 10 items. The user gets a better response time in getting the first 10 and while reviewing those records, the next 10 can be pulled down from the service in the background. To save bandwidth, you can force the user to manually indicate that they want the next set (think Google paging).

To support paging in service operations, the operation needs to be passed a page index and a page size.

Only provide a single data contract parameter to an operation or a single value type if only one value is required.

This isn't actually anything to do with performance or data size limits. It is more a code churn issue. Service contracts tend to evolve a lot during their first few development cycles. It is so much easier for consumers to deal with modified data contracts rather than changing service operation signatures, especially for generated client proxies.

Never bake your security into the service operation.

Again this is not related to performance or size limits. This is simply good design. Don't constraint your service design to a specific security model. The most common example is passing a username and password to a service in order to access some back-end resource. There are better ways around this. With regard to the username/password example, see an alternative in this post.

Move your exception management and exception shielding from your service implementation into an IErrorHandler implementation.

See this post for more information about wiring up IErrorHandler implementations using configuration.

If your IErrorHandler implementation (or at least one of them) is doing exception shielding, you may consider applying the error handler as an attribute of the service implementation class rather than leaving it up to configuration. There is a risk with configuration that it will be missing, or incorrect configured such that you don't have exception shielding protecting your service. This may represent a security risk.

See this post for more information about wiring up IErrorHandler implementations by compiling them against the service implementation using an attribute.

Conclusion

These are just a few ideas. Another way of checking for appropriate service contract design is to calculate the maximum amount of bytes that a message will contain as defined by the data contract passed to or returned from a service operation. If the answer is that the amount can't be determined, then that should raise red flags. This will be a risk that needs to be managed, such as paging of array based types. If it is determined that the message is too large for WCF default limits, then the granularity of the data contracts needs to be reviewed (chatty vs chunky data contracts and operations). If it can be determined that some information is used more often that other information for an entity, then consider implementing summary data contracts.

 

Updated

Formatting and information about IErrorHandler has been updated.

Cache expiration policies article

I was asked recently about caching expiration policies in response to my rant in my Caching should not be the source of truth post and a comment I made in a post by Chris Blankenship. I have written an article about cache expiration policies which you can find here. It discusses the high level concepts in expiration polices and some suggestions about which options to pick. It makes references to HttpRuntime.Cache and the Caching Application Block in EntLib.

Cache Expiration Policies

This article aims to provide an overview about caching expiration policies and how they can be used. While the concepts are technology agnostic, the article references the System.Web.Caching.Cache exposed from HttpRuntime for code examples and also some references to the Caching Application Block in EntLib.

What is a cache expiration policy?

A cache expiration policy is a combination of concepts which define when a cache entry expires. Once a cache entry has expired, it may be removed from a cache. The policy is typically assigned when data is added to the cache and is normally custom to a single cached entry based on characteristics of the entry.

The specific implementation of a cache expiration policy depends on the caching framework and the requirements of the cache data. Typically, a cache expiration policy is defined by using one or more of the following concepts:

  • Absolute expiration
  • Sliding expiration
  • Cache dependency
  • Cache priority

If more than one of these concepts are used for a cache entry, then the entry is likely to expire because of one part of the expiration policy has been processed before another part of the policy is required to expire the entry. As each part of the policy is evaluated, the first part that requires an expiration of the cache entry will cause the expiration to occur regardless of whether other parts of the policy are outstanding.

Absolute expiration

Absolute expiration refers to a specific point in time when the cache entry will expire. Once that point in time has elapsed, the cache entry is expired and can be removed from the cache.

In the following example, an entry is added to the cache with a cache expiration policy that defines an absolute expiration of 25th December 2008 at 4:30pm. Once 4:30pm rolls around on that day, the entry will be expired. Assuming the item is added to the cache on 25th December 2008 at 4:00pm, this policy defines that the cache entry will only be alive for 15 minutes.

using System;

using System.Web;

using System.Web.Caching;

namespace ConsoleApplication1

{

    internal class Program

    {

        private static void Main(String[] args)

        {

            DateTime absoluteExpiration = new DateTime(2008, 12, 25, 16, 15, 00);

            HttpRuntime.Cache.Add(

                "AbsoluteCacheKey",

                "This is the data to cache",

                null, // No cache dependency defined

                absoluteExpiration,

                Cache.NoSlidingExpiration,

                CacheItemPriority.Normal,

                null); // No callback defined

        }

    }

}

Sliding expiration

Sliding expiration refers to a span of time in which the cache entry must be retrieved from the cache in order to prevent expiration.

In the following example, an entry is added to the cache with a cache expiration policy that defines a sliding expiration of 5 minutes. The entry will stay in the cache as long as it is read within 5 minutes of the previous read. As soon as 5 minutes elapse without a read of that item from the cache, the entry will be expired.

using System;

using System.Web;

using System.Web.Caching;

namespace ConsoleApplication1

{

    internal class Program

    {

        private static void Main(String[] args)

        {

            TimeSpan slidingExpiration = new TimeSpan(0, 5, 0);

            HttpRuntime.Cache.Add(

                "SlidingCacheKey",

                "This is the data to cache",

                null, // No cache dependency defined

                Cache.NoAbsoluteExpiration,

                slidingExpiration,

                CacheItemPriority.Normal,

                null); // No callback defined

        }

    }

}

Cache dependency

Cache dependencies are references to other information about the cache entry. The dependency might be on a file or database record. When the dependency has changed, the cache entry is expired. The most common scenario of cache dependencies is a dependency on a file path for data loaded from that file.

In the following example, file data is read from disk and added to the cache with a cache expiration policy that defines a dependency on the file path. When the file is updated (usually out of process), the file change event detected by the cache dependency will cause the entry to be expired.

using System;

using System.IO;

using System.Web;

using System.Web.Caching;

namespace ConsoleApplication1

{

    internal class Program

    {

        private static void Main(String[] args)

        {

            const string filename = @"C:\test.xml";

            CacheDependency dependency = new CacheDependency(filename);

            String contents = File.ReadAllText(filename);

            HttpRuntime.Cache.Add(

                "DependencyCacheKey",

                contents,

                dependency,

                Cache.NoAbsoluteExpiration,

                Cache.NoSlidingExpiration,

                CacheItemPriority.Normal,

                null); // No callback defined

        }

    }

}

Cache priority

Cache priority indicates the importance of the data relative to other cache entries. This is used to determine which items to expire in the cache first when system resources become scarce.

In the following example, a low cache priority is defined. This cache entry will be expired before other entries that have a higher priority.

using System;

using System.IO;

using System.Web;

using System.Web.Caching;

namespace ConsoleApplication1

{

    internal class Program

    {

        private static void Main(String[] args)

        {

            const string filename = @"C:\test.xml";

            CacheDependency dependency = new CacheDependency(filename);

            String contents = File.ReadAllText(filename);

            HttpRuntime.Cache.Add(

                "DependencyCacheKey",

                contents,

                dependency,

                Cache.NoAbsoluteExpiration,

                Cache.NoSlidingExpiration,

                CacheItemPriority.Low,

                null); // No callback defined

        }

    }

}

When are items actually flushed from the cache?

Most caching frameworks will only remove expired items from the cache when system resources are scarce or when the cache is referenced. This means that a cache entry that has expired due to an absolute or sliding expiration may not be removed from the cache until some future time which may be well after the entry actually expired.

This is done for performance. The caching frameworks normally use a scavenging algorithm that looks for expired entries and removes them. This is typically invoked when the cache is referenced rather than when the items actually expire. This allows the cache framework to avoid having to constantly track time based events to know when to remove items from the cache.

HttpRuntime.Cache vs Caching Application Block

There are two main differences between these caching frameworks. Firstly, the Caching Application Block in EntLib allows you to define both an absolute expiration and a sliding expiration for an expiration policy while HttpRuntime.Cache only supports one or the other. Secondly, EntLib requires a decent amount of configuration whereas HttpRuntime.Cache works out of the box.

Which one to use? Well it depends. Here are some things to consider:

  • Advice from Scott Guthrie and his team is that HttpRuntime.Cache may be used in non-ASP.Net scenarios.
  • Do you need the flexibility of both absolute and sliding expiration?
    • If you do, prefer EntLib.
    • If you don't, probably prefer HttpRuntime.Cache.
  • Is your code conducive to configuration requirements?
    • For example, framework type components you write that use caching probably shouldn't bundle a requirement on consumers of the assembly to put specific configuration in their application config files. In these cases, prefer HttpRuntime.Cache.

Policy suggestions

What should you use for your policy? It typically depends on answers to the following questions:

  1. How often is the data read?
  2. A cache entry that is read very often will suit a sliding expiration. This allows for fluctuations in the frequency of reads. If the frequency reduces (say overnight), then it will expire.

    A cache entry that is not read very often will suit an absolute expiration. This allows the data to be around for a while, just in case it is referenced, but forces it to expire at some point.

  3. How often is the data changed?
  4. A cache entry that is changed often will suit a cache dependency if supported. A custom cache dependency may be required in this case. Without a cache dependency, a short absolute expiration would be appropriate.

    A cache entry that is not changed often will suit either a sliding or absolute expiration depending on the answers to the other questions. Ideally, both a sliding and absolute expiration would be used.

  5. How large is the data?
  6. A cache entry that is a large amount of data will suit a low priority. This will allow the system to expire the cache entry when it has scarce resources, namely RAM. A sliding expiration would also be a good combination with large data. If it is not referenced for a while, it is good to release this memory.

    A cache entry that is not a large amount of data may use a high priority if it is read more often than other entries. It is best to leave the cache priority as the default value unless the entry has different characteristics compared to other entries based on answers to the above questions.

Ideally a combination of these concepts will be used to build a policy. A good combination is sliding expirations that also have an absolute expiration. Priorities should only be assigned for cache entries that are different to the others (frequency of reads, importance of data or the size of it). Cache dependencies are good, but are not always appropriate. The combinations available are also restricted to the caching framework used.

Snippets 1.2 released

Snippets 1.2 contains two bug fixes for [BE]. The first bug was serious in that the extension was stripping contents that occur before the first html tag for snippets that don't require brackets. The second bug was minor in that the extension would not inject snippets which don't require brackets when the content doesn't contain any html tags.

Get the latest code from here.

Getting BE.Net to support IIS7 and the integrated pipeline

Here is another easy one. Update the web.config of [BE] to include the following.

<system.webServer>

  <security>
    <requestFiltering allowDoubleEscaping="True"/>
  </security>

  <modules runAllManagedModulesForAllRequests="true">
    <add name="WwwSubDomainModule" type="BlogEngine.Core.Web.HttpModules.WwwSubDomainModule, BlogEngine.Core"/>
    <add name="UrlRewrite" type="BlogEngine.Core.Web.HttpModules.UrlRewrite, BlogEngine.Core"/>
    <add name="CompressionModule" type="BlogEngine.Core.Web.HttpModules.CompressionModule, BlogEngine.Core"/>
    <add name="ReferrerModule" type="BlogEngine.Core.Web.HttpModules.ReferrerModule, BlogEngine.Core"/>
    <!--Remove the default ASP.NET modules we don't need-->
    <remove name="PassportAuthentication"/>
    <remove name="Profile"/>
    <remove name="AnonymousIdentification"/>
  </modules>

  <handlers>
    <add verb="*" name="File" path="file.axd" type="BlogEngine.Core.Web.HttpHandlers.FileHandler, BlogEngine.Core" />
    <add verb="*" name="Image" path="image.axd" type="BlogEngine.Core.Web.HttpHandlers.ImageHandler, BlogEngine.Core" />
    <add verb="*" name="Syndication" path="syndication.axd" type="BlogEngine.Core.Web.HttpHandlers.SyndicationHandler, BlogEngine.Core" />
    <add verb="*" name="Sitemap" path="sitemap.axd" type="BlogEngine.Core.Web.HttpHandlers.SiteMap, BlogEngine.Core" />
    <add verb="*" name="Trackback" path="trackback.axd" type="BlogEngine.Core.Web.HttpHandlers.TrackbackHandler, BlogEngine.Core" />
    <add verb="*" name="Pingback" path="pingback.axd" type="BlogEngine.Core.Web.HttpHandlers.PingbackHandler, BlogEngine.Core" />
    <add verb="*" name="OpenSearch" path="opensearch.axd" type="BlogEngine.Core.Web.HttpHandlers.OpenSearchHandler, BlogEngine.Core" />
    <add verb="*" name="Metaweblog" path="metaweblog.axd" type="BlogEngine.Core.API.MetaWeblog.MetaWeblogHandler, BlogEngine.Core" />
    <add verb="*" name="RSD" path="rsd.axd" type="BlogEngine.Core.Web.HttpHandlers.RsdHandler, BlogEngine.Core" />
    <add verb="*" name="CSS" path="css.axd" type="BlogEngine.Core.Web.HttpHandlers.CssHandler, BlogEngine.Core" />
    <add verb="*" name="JS" path="js.axd" type="BlogEngine.Core.Web.HttpHandlers.JavaScriptHandler, BlogEngine.Core" />
    <add verb="*" name="Rating" path="rating.axd" type="BlogEngine.Core.Web.HttpHandlers.RatingHandler, BlogEngine.Core" />
    <add verb="*" name="OPML" path="opml.axd" type="BlogEngine.Core.Web.HttpHandlers.OpmlHandler, BlogEngine.Core" />
    <add verb="*" name="BlogML" path="blogml.axd" type="BlogEngine.Core.Web.HttpHandlers.BlogMLExportHandler, BlogEngine.Core" />
    <add verb="*" name="SIOC" path="sioc.axd" type="BlogEngine.Core.Web.HttpHandlers.Sioc, BlogEngine.Core" />
    <add verb="*" name="APML" path="apml.axd" type="BlogEngine.Core.Web.HttpHandlers.Apml, BlogEngine.Core" />
    <add verb="*" name="FOAF" path="foaf*.axd" type="BlogEngine.Core.Web.HttpHandlers.Foaf, BlogEngine.Core" />
  </handlers>

  <validation validateIntegratedModeConfiguration="false" />

</system.webServer>

Get BE.Net to recognise the iPhone

Here is an easy one. Update the web.config to include iphone in the regular expression used by [BE] to determine whether the browser is a mobile device.

<!-- The regex used to identify mobile devices so a different theme can be shown -->
<add key="BlogEngine.MobileDevices" value="(iphone|nokia|sonyericsson|blackberry|samsung|sec\-|windows ce|motorola|mot\-|up.b|midp\-)"/>

IServiceLocator - A common IoC container / Service locator interface

This is great news - My Technobabble : IServiceLocator a step toward IoC container / Service locator detente. This diverse group of people have been able to collaborate to create a common interface to use for IoC container / Service locator frameworks. Hopefully the adoption rate will be swift by the creators of the IoC frameworks. Congratulations guys, this is a great achievement.

It would be good to see the same outcome for a common logging interface.