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 its 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 to the implementation is service-side Web.config to declare behavior extension section and extension itself:
="1.0"="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;
}
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)
{
}
}
}
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
{
public void Initialize()
{
}
}
}
History
- 14/12/2012 - Version 1.0 - Initial publication