Table of Contents for SharePoint as a WCF host.
- Basics -
1. Create a WCF Home. This is the virtual directory that will host all your WCF endpoints.
2. Create a WCF Service Library, and throw it in the GAC.
3. Create a relevant .svc file in the WCF home you created in step #1.
4. Write a WCF Virtual Path Provider, and register it in the SharePoint site.
- Real world -
1. Adding WCF Support to a website.
2. Deploying WCF EndPoints as solutions.
As I just mentioned, the SPVirtualPathProvider blows up when it see's a URL that starts with '~'. Luckily, the problem is easy to fix. You simply author your own Virtual Path Provider.
In order to write your own VirtualPathProvider, you have to author a class that inherits from the System.Web.Hosting.VirtualPathProvider class. Now, you might be thinking that this will be a very difficult job - considering the SPVirtualPathProvider does SO MUCH work in making the content database paths, and physical paths work together. If I had to do all that work, I'd be howling and screaming like a hungry dog that just got kicked. Thankfully, the VirtualPathProviders work in a chained form - so all you've gotta do is, chain yours AFTER the SPVirtualPathProvider. (I'll talk soon about how to register your Virtual Path Provider).
This way, the previous VirtualPathProvider (in our case, the SPVirtualPathProvider), is accessible using the "Previous" property on the VirtualPathProvider class.
Thus, my custom VirtualPathProvider looks like this -
1: public class WCFVirtualPathProvider : VirtualPathProvider
2: { 3: public override string CombineVirtualPaths(string basePath, string relativePath)
4: { 5: return Previous.CombineVirtualPaths(basePath, relativePath);
6: }
7:
8: // all other methods omited, they simply call Previous... like the above.
9:
10: public override bool FileExists(string virtualPath)
11: { 12: string fixedVirtualPath = virtualPath;
13: if (
14: virtualPath.StartsWith("~") && 15: virtualPath.EndsWith(".svc") 16: )
17: { 18: fixedVirtualPath = virtualPath.Remove(0, 1);
19: }
20: return Previous.FileExists(fixedVirtualPath);
21: }
22:
23: protected override void Initialize()
24: { 25: base.Initialize();
26: }
27: }
So really, my funky logic goes in a single method - the FIleExists method.
Okay, fantastico. The next question is, how the heck do I register this VirtualPathProvider into my SharePoint / Port 80 site? Well the code to do so is quite simple as shown below:
1: WCFVirtualPathProvider wcfVPP = new WCFVirtualPathProvider();
2: HostingEnvironment.RegisterVirtualPathProvider(wcfVPP);
But, the above must be executed ONLY once. ONLY Once - after each IISRESET :-). Yep, ASP.NET forgets the virtual path providers it was using if you issue an IISReset, or modify the web.config - basically everytime the application recompiles.
So, the mostest bestest place to put such code, is inside an HttpModule, and use a static variable to check and see that you don't register this VirtualPathProvider over and over again. Here's the code for the HttpModule -
1: public class WCFVPPRegModule: IHttpModule
2: { 3: static bool wcfProviderInitialized = false;
4: static object locker = new object();
5:
6: public void Init(HttpApplication context)
7: { 8: if (!wcfProviderInitialized)
9: { 10: lock (locker)
11: { 12: if (!wcfProviderInitialized)
13: { 14: WCFVirtualPathProvider wcfVPP = new WCFVirtualPathProvider();
15: HostingEnvironment.RegisterVirtualPathProvider(wcfVPP);
16: wcfProviderInitialized = true;
17: }
18: }
19: }
20: }
21: }
Ok good job.
Now put both of the above in the GAC (I put them both as 2 classes in a single assembly called WCF Support). Modify the web.config to register the HttpModule - which in turn will register the WCFVirtualPathProvider. This is as shown below:
1: <add
2: name="WCFVPPRegModule"
3: type="WCFSupport.WCFVPPRegModule, WCFSupport, Version=1.0.0.0, Culture=neutral,PublicKeyToken=d4f7bd9e55b39661"
4: />
Windows 2008 Only - In IIS7 Mgr, double click on authentication settings for the SharePoint website you're fiddling with, and "Enable" anonymous authentication. Before you freak out, all this is doing is enabling IUSR to authenticate to the website - your SharePoint site is still protected, non-anonymous. But if you don't believe me, go check it yourself in SharePoint central admin, and by browsing to the site itself. If you don't wanna do this, you'll have to make _wcf as it's own Virtual Directory, and put anon only there. But the VirtualPathProvider will quit working if you do that. Basically the VirtualPathProvider will be given a URL such as ~/HelloWorld.svc, instead of ~/_wcf/HelloWorld.svc, and the SharePoint VirtualPathProvider will look for http://yourmosssite/HelloWorld.svc - and obvious fail. Unfortunately, you don't quite have the HttpContext ready to differentiate between a virtual directory and a website URL, so you'll just have to go with the not so cool choice of dumbing down your VirtualPathProvider to .. "If I see .svc - always return true" -- i.e. Assume File Always Exists --> Not recommended frankly.
NICE.
Now, browse to your WCF service, and you should merrily see the below -
.. Umm .. that's basically it. You can now start using the SharePoint object model in the WCF service, and merrily expose SharePoint functionality on a variety of endpoints. Now THATS cool!