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! 😉
EDIT:
Since WCF 4.0 there is another way to achieve the same thing. It is called “file less activation”. 🙂
Example: