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

Custom WCF web service warm-up regardless service host

, 14 Mar 2013
Rate this:
Please Sign up or sign in to vote.
Implementing WCF behavior extension to run code on service launch.

Introduction 

There are many different real-world scenarios requiring a web service warm-up, i.e., run custom code once the service is launched and don't on subsequent requests servicing. An obvious example is IoC container initialization.

Background   

Currently there are many ways to host a WCF service, such as Windows Service Self-Hosting, IIS Web Application, Windows Process Activation, local development/debug using Visual Studio Development Server or IIS Express, and fortunately many others.

The good news is almost each of them supports custom service warm-up technique, and the bad news is each supports it's own way to do this.

The configuration

The solution I've developed is pretty straight forward and based on the built-in WCF functionality to support various extension points. Such as custom service host, custom endpoints, behavior extensions, etc. My one is about to implement a custom behavior extension.

As I far as I can see WCF always (or almost always) provides two ways to achieve the same goal: using code, usually attribute decoration, or using configuration. As a person with strong enterprise background having experience working with leviathan-like business applications controlled by enormous configuration layer, I still like configuration way. It just requires additional attention in details. But code does just the same. So below I will use configuration where possible.

The entry point the the implementation is service-side Web.config to declare behavior extension section and extension itself:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="serviceInitialization"
             type="Project.Services.ServiceInitializationBehaviorSection, Project.Core" />
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceInitialization type="Project.Services.ServiceInitialization, Project.Services" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration> 

As you can observe section attribute name value corresponds to section opening tag name (e.g. serviceInitialization). 

The code

Now we need to write an in-code representation of the section using WCF Configuration API and implement BehaviorExtensionElement abstract class:

namespace Project.Services
{
    public class ServiceInitializationBehaviorSection : BehaviorExtensionElement
    {
        private readonly Lazy<ConfigurationPropertyCollection> _properties =
            new Lazy<ConfigurationPropertyCollection>(() =>
            {
                return new ConfigurationPropertyCollection
                {
                    new ConfigurationProperty("type",
                                              typeof(string),
                                              String.Empty,
                                              null,
                                              new StringValidator(Int32.MinValue,
                                                                  Int32.MaxValue,
                                                                  null),
                                              ConfigurationPropertyOptions.IsRequired)
                };
            });

        public override Type BehaviorType
        {
            get { return typeof(ServiceInitializationBehavior); }
        }

        [StringValidator(MinLength = Int32.MinValue,
                         MaxLength = Int32.MaxValue),
                         ConfigurationProperty("type", DefaultValue = "")]
        public string InitializationType
        {
            get { return (string)base["type"]; }
            set { base["type"] = value; }
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get { return _properties.Value; }
        }

        public override void CopyFrom(ServiceModelExtensionElement from)
        {
            base.CopyFrom(from);

            var element = (ServiceInitializationBehaviorSection)from;
            InitializationType = element.InitializationType;
        }

        protected override object CreateBehavior()
        {
            var type = Type.GetType(InitializationType);
            if (type == null)
                throw new InvalidOperationException("Can't get read type from initialization type given");

            var serviceInitialization = Activator.CreateInstance(type) as IServiceInitialization;
            if (serviceInitialization == null)
                throw new InvalidOperationException("Can't create object from initialization type given");

            return new ServiceInitializationBehavior(serviceInitialization);
        }
    }
} 

and of behavior extension itself implementing IServiceBehavior interface:

namespace Project.Services
{
    public class ServiceInitializationBehavior : IServiceBehavior
    {
        private readonly IServiceInitialization _serviceInitialization;

        public ServiceInitializationBehavior(IServiceInitialization serviceInitialization)
        {
            if (serviceInitialization == null)
                throw new ArgumentNullException("serviceInitialization");
            _serviceInitialization = serviceInitialization;	
        }

        #region IServiceBehavior members
        public void AddBindingParameters(ServiceDescription serviceDescription,
                                         ServiceHostBase serviceHostBase,
                                         Collection<ServiceEndpoint> endpoints,
                                         BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
                                          ServiceHostBase serviceHostBase)
        {
            _serviceInitialization.Initialize();
        }

        public void Validate(ServiceDescription serviceDescription,
                             ServiceHostBase serviceHostBase)
        {
        }
        #endregion
    }
} 

The logic is simple: instantiate behavior extension and pass into its constructor an object specified in the configuration by FQN using the type attribute. This code will be run once on service launch, its configuration initialization.

Here is this custom interface declaration: 

namespace Project.Services
{
    public interface IServiceInitialization
    {
        void Initialize();
    }
}

and the article's key code - custom interface implementation:

namespace Project.Services
{
    public class ServiceInitialization : IServiceInitialization
    {
        #region IServiceInitialization members
        public void Initialize()
        {
            // your service warm-up logic here
        }
        #endregion
    }
}

History 

  • 14/12/2012 - Version 1.0 - The initial publication.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Alexander Batishchev
Software Developer Microsoft Open Tech
United States United States
.NET developer and architect
Follow on   Twitter

Comments and Discussions

 
Questionweb.config? Pinmembermstaros14-Dec-13 0:54 
AnswerRe: web.config? PinmemberAlexander M. Batishchev14-Dec-13 10:30 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 14 Mar 2013
Article Copyright 2012 by Alexander Batishchev
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid