Dynamic IIS hosted WCF Service

By | November 21, 2010

For hosting WCF Services in IIS you usually need a .svc file. This File exposes your Service.
With ASP.NET virtual Paths you have the opportunity to load your service without a .svc file.
You can put the WCF Service inside a DLL without adding a reference to the ASP.NET WebApp.

Implementation:

– Create a Class derived from VirtualPathProvider and override the Methods GetFile and FileExists.
– Create a Class derived from VirtualFile and override the Method Open.
– Create a Class derived from ServiceHostFactory and override the Method CreateServiceHost.
– The VirtualPathProvider manages the calling URLs and returns a VirtualFile, if the client calls a WCF Service.
– The VirtualFile generates the .svc Content.
– The ServiceHostFactory hosts the WCF Service.
– Register the VirtualPathProvider in global.asax

Here is the code: (VirtualPathProvider)

 
public class ServicePathProvider : VirtualPathProvider
{
    public override VirtualFile GetFile(string virtualPath)
    {
        if (!this.IsServiceCall(virtualPath))
            return this.Previous.GetFile(virtualPath);
        return new ServiceFile(virtualPath);
    }
        
    private bool IsServiceCall(string virtualPath)
    {
        // Check if it is a wcf service call
        virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
        return (virtualPath.ToLower().StartsWith("~/srv_"));
    }

    public override bool FileExists(string virtualPath)
    {
        if (!this.IsServiceCall(virtualPath))
            return this.Previous.FileExists(virtualPath);
        return true;
    }
}

VirtualFile

 
public class ServiceFile : VirtualFile
{
    public ServiceFile(string virtualPath) 
        : base(virtualPath) 
    { }

    public string GetCallingServiceName
    {
        get 
        {
            // Return class name. srv_hello.svc => hello
            return base.VirtualPath.Replace("/srv_", string.Empty)
                        .Replace(".svc", string.Empty).ToLower(); 
        }
    }

    public string GetService()
    {

        string srv = this.GetCallingServiceName;
        // hello => Hello
        return srv[0].ToString().ToUpper() + srv.Substring(1);
    }

    public override Stream Open()
    {
        var serviceDef = new MemoryStream();
        var defWriter = new StreamWriter(serviceDef);
            
        // Write host definition
        defWriter.Write("<%@ ServiceHost Language=\"C#\" Debug=\"true\" Service=\"Services."+ this.GetService() + "\"  " +
                        "Factory=\"DynamicServices.DynamicHostFactory, DynamicServices\" %>");
        defWriter.Flush();
            
        serviceDef.Position = 0;
        return serviceDef;
    }
}

ServiceHostFactory

public class DynamicHostFactory : ServiceHostFactory
{
    public DynamicHostFactory() { }

    public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
    {
        // Load bin/services.dll
        var asm = Assembly.Load("Services");
        var serviceType = asm.GetType(constructorString);
        var host = new ServiceHost(serviceType, baseAddresses);

        // Add endpoints. (In this example only IHello interface)
        foreach (Type contract in serviceType.GetInterfaces())
        {
            var attribute = (ServiceContractAttribute)
                Attribute.GetCustomAttribute(contract, typeof(ServiceContractAttribute));
            if (attribute != null)
                host.AddServiceEndpoint(contract, new BasicHttpBinding(), "");
        }
        // Add metdata behavior for generating wsdl
        var metadata = new ServiceMetadataBehavior();
        metadata.HttpGetEnabled = true;
        host.Description.Behaviors.Add(metadata);
            
        return host;
    }
}

Global.asax

void Application_Start(object sender, EventArgs e)
{
    HostingEnvironment.RegisterVirtualPathProvider(new ServicePathProvider());
}

Example:
I created a DLL (Services.dll) with a WCF service. Here the Code:

[ServiceContract]
public interface IHello
{
    [OperationContract]
    string SayHello(string name);
}

public class Hello : IHello
{
    public string SayHello(string name)
    {
        return "Hello " + name;
    }
}

I compiled the DLL and copied it into the bin Folder of the ASP.NET Application.
When I start the WebApp and call the URL “http://localhost:{Port}/srv_hello.svc” the Service UI of the Hello Service (Services.dll) will be generated. Voila! 😉
WCF Service (DLL)

EDIT:
Since WCF 4.0 there is another way to achieve the same thing. It is called “file less activation”. 🙂
Example: