Installing a WCF Service on a Shared Hosting Site, Revisited

I wrote a post not too long ago discussing How to Install a WCF Service on a GoDaddy hosted site. In it, I presented a couple of different ways to get around the “This collection already contains an address with scheme http” error, which looks like:

image

While those methods do work, I found myself in perhaps a special situation where only a combination of those solutions resolved my particular problem. Let me explain by first detailing my hosting situation.

My Hosting Schema

I have two sites: scottmarlowe.com and itscodingtime.com. Both are hosted with GoDaddy.com under their Deluxe Hosting Plan, which basically means I have multiple domain names that share server space. One is the “root”; any other domains exist in a folder beneath the root. That last point is important as we get into this.

In my case, my “root” domain is scottmarlowe.com. The sub-domain of interest here is this site, itscodingtime.com.

The Situation

I’ve deployed a new Silverlight TagCloud control (upgraded to Silverlight 3, of course) to both sites. Each site has its own ClientBin where the control is housed. Now, each Silverlight control accesses their own individual copies of the same WCF service. In other words, I have a .svc and accompanying DLL hosted on each site. Therein begins the problem.

The Problem

Now, I’d already uploaded all the new files to the sub-site the other day and everything was working fine on both root and sub-sites. The problem came about this morning when I did the same update (Silverlight and WCF components) to the root site, then uploaded the web.config (which, in retrospect, I wonder if really needed updating). It was the web.config update that did it. I started seeing the “This collection already contains an address with scheme http” error on the sub-site (itscodingtime.com).

The Solution

I had chosen to modify both web.config’s as my previous post suggested. Clearly this was no longer working, so I took a hybrid approach. I left the root’s web.config alone, but modified the sub-site’s (which, at this time, was the one not working).

First, I removed the entire “serviceHostingEnvironment” section from the sub-site’s web.config.

Second, for the sub-site only, I went back to the CustomHostFactory approach by creating a new class file called “CustomHostFactory.cs”. This file goes in the site’s App_Code folder. It’s full contents are:

  1:
  2: using System;
  3: using System.ServiceModel;
  4: using System.ServiceModel.Activation;
  5:
  6: class CustomHostFactory : ServiceHostFactory
  7: {
  8:   protected override ServiceHost CreateServiceHost (Type serviceType, Uri[] baseAddresses)
  9:   {
 10:     // Specify the exact URL of your web service
 11:     var webServiceAddress = new Uri (
 12:       "/itscodingtime/TagCloudService.svc");
 13:
 14:     return (new CustomHost (serviceType, webServiceAddress));
 15:   }
 16: }
 17:
 18: public class CustomHost : ServiceHost
 19: {
 20:   public CustomHost (Type serviceType, params Uri[] baseAddresses) : base (serviceType,
 21:     baseAddresses)
 22:   { }
 23: }
View Plain

One notable difference between this implementation and the one suggested in the other post is that this one explicitly specifies the url of the WCF service (but we’re not quite done; see code below).

Also, note the address (line 11): /itscodingtime/TagCloudService.svc

That extra “itscodingtime” folder is necessary because of my hosting situation as described above. In fact, if I leave it out, and instead specify “/TagCloudService.svc” (which seems more logical) as the WCF service url, I start seeing “There is no compatible TransportManager found for URI” errors:

sshot-72

So, I have to specify the full url with the extra “itscodingtime” folder, which actually corresponds to the folder it is stored at under the root.

Back to the CustomHostFactory. If we leave it as above we’ll soon run into problems while running against localhost. So, the final version is this:

  1:
  2: using System;
  3: using System.ServiceModel;
  4: using System.ServiceModel.Activation;
  5:
  6: class CustomHostFactory : ServiceHostFactory
  7: {
  8:   protected override ServiceHost CreateServiceHost (Type serviceType, Uri[] baseAddresses)
  9:   {
 10:     // Specify the exact URL of your web service
 11:     var webServiceAddress = baseAddresses[0].ToString ().Contains ("localhost")
 12:       ? baseAddresses[0]
 13:       : new Uri ("/itscodingtime/TagCloudService.svc");
 14:
 15:     return (new CustomHost (serviceType, webServiceAddress));
 16:   }
 17: }
 18:
 19: public class CustomHost : ServiceHost
 20: {
 21:   public CustomHost (Type serviceType, params Uri[] baseAddresses) : base (serviceType, baseAddresses)
 22:   { }
 23: }
View Plain

Lines 11-13 will set our WCF service address based on whether we are running locally or if we’re in production.

As a final step, remember to add the “Factory” attribute to the .svc’s “ServiceHost” and you’re set.

As you can see, I can once more access my WCF service:

sshot-73