Home
Atom Feed

Autoloading RESTful WCF services

I am currently in the process of developing a service for Chrono, for storing data from the phone on the web. Creating secure WCF services is not difficult, you just need a subdomain, a SSL certificate and some configuration work. But good certificates cost money, and I know that I will be making services for other apps in the future. Although I would really love to buy a wildcard SSL certificate, it is a little bit over my private budget.

So I decided to build a service host that could host several different WCF services. I didn't want to recompile the host every time I create a new WCF service, so I needed something of an autoloader feature. I love easy deployments, so what could be easier than dropping a service assembly in the bin folder and get it up and running automatically in a few seconds? As it turned out, it was not very difficult to achieve just that.

The purpose of this code is to make an assembly that can act as a base for your own implementation. After the base assembly part, there is a sample implementation. You can also download the source code if you want to take a look in Visual Studio instead.

The ServiceHost base assembly

First off, create a new Visual Studio Class Library project.

Then we need to create an attribute that will be used to decorate the WCF service classes. Creating attributes is easy, and here is the code for our attribute:

namespace EyeCatch.ServiceHostFactory.Core.Attributes
{
    [AttributeUsage(AttributeTargets.Class)]
    public class AutoLoadWebServiceAttribute : Attribute
    {
        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>
        /// The name.
        /// </value>
        public string Name { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="AutoLoadServiceAttribute"/> class.
        /// </summary>
        public AutoLoadServiceAttribute()
        {
            Name = string.Empty;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AutoLoadServiceAttribute"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        public AutoLoadServiceAttribute(string name)
        {
            this.Name = name;
        }
    }
}

Notice that Name property? We'll use that to specify the service url later.

Next we need to create the HttpApplication base:

namespace EyeCatch.ServiceHostFactory.Core
{
    public class AutoLoadHttpApplicationBase : HttpApplication
    {
        /// <summary>
        /// Handles the Start event of the Application control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void Application_Start(object sender, EventArgs e)
        {
            RegisterRoutes();
        }

        /// <summary>
        /// Registers the routes.
        /// </summary>
        internal void RegisterRoutes()
        {
            foreach (var webservice in GetTypesWith<AutoLoadServiceAttribute>(false))
            {
                // Get service name
                var attribute = webservice.GetCustomAttributes(false).Single(x => x is AutoLoadServiceAttribute) as AutoLoadServiceAttribute;

                RouteTable.Routes.Add(new ServiceRoute(attribute.Name, new WebServiceHostFactory(), webservice));
            }
        }

        /// <summary>
        /// Gets the types with the specified attributes.
        /// </summary>
        /// <typeparam name="TAttribute">The type of the attribute.</typeparam>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns></returns>
        internal IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) where TAttribute : Attribute
        {
            return from a in AppDomain.CurrentDomain.GetAssemblies()
                   from t in a.GetExportedTypes()
                   where t.IsDefined(typeof(TAttribute), inherit)
                   select t;
        }
    }
}

And that is all there is to it!
Every service that implements the [AutoLoadService] attribute will now get loaded by the AutoLoadHttpApplicationBase when the Application Pool recycles (which means every time you touch the web.config or drop/change a file in the bin folder).

Sample implementation

When creating services, I always start with making the interface.
You do use interfaces right? While it's considered best practice it might not always be the right thing. I always recommend doing it, as it takes only five minutes and saves you a load of trouble later on if your service gets more complex.

Projects are like tiger cubs. They are easy to handle when they're young, but when they mature they get quite menacing and difficult. :-)

But I digress. Here is the sample interface and implementation:
Note that this should be a separate/new visual studio project:

namespace EyeCatch.ServiceHostFactory.Sample.Service.Interfaces
{
    [ServiceContract]
    interface ISampleService
    {
        [OperationContract]
        string HelloWorld();
    }
}

namespace EyeCatch.ServiceHostFactory.Sample.Service
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [AutoLoadService("HelloWorldSampleService")]
    public class SampleService : ISampleService
    {
        #region Implementation of ISampleService
 
        [WebGet]
        public string HelloWorld()
        {
            return "Hello World!";
        }
 
        #endregion
    }
}

Now for the implementation of AutoLoadHttpApplicationBase you need to create a new project and add a global.asax. (CTRL + SHFT + A -> Web -> Global Application Class -> Add).
When you've done that, open the file and set the Global class to inherit from AutoLoadHttpApplicationBase:

public class Global : AutoLoadHttpApplicationBase
{
    ...
}

Then add the following line to the Application_Start method:

base.Application_Start(sender, e);

Now ensure that your web.config looks like this:
(If you created a project of type WCF Service Application, then the only thing you need to fix is the aspNetCompatibilityEnabled attribute as it's not there by default)

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  
</configuration>

The sample service is now done, and now we can set up the web site.

Setting up the service

I love using WebMatrix for testing my projects, you should give it a try as well. It makes setting up test web sites a breeze.

Start WebMatrix and create a "Site from folder" and browse to the project folder you created last. (The one with the global.asax)

Drop the assembly output from the second project to the "bin" folder in the site you just created.

Browse to the url set by WebMatrix and add "HelloWorldSampleService/help" to the url. If everything is set up correctly, you should now see a page like this:

Not hard eh?