Installing a WCF Service on a Shared Hosting Site, Revisited

July 13, 2009 20:30

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 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:       "http://www.itscodingtime.com/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: }

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): http://www.itscodingtime.com/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 "http://www.itscodingtime.com/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 ("http://www.itscodingtime.com/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: }

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

Conclusion

If you've got a similar situation where you host multiple domains under the same root location and you find yourself seeing similar errors, then the method described above should get your services up and running again.


The difference between Visual Studio's WCF Service Application and WCF Service Library project templates

July 7, 2009 08:32

I've been working with WCF lately. One of the immediate points of confusion was which type of project to start out with: WCF Service Application or WCF Service Library. Let's take a look at each, then examine the differences. Hopefully we can come to some conclusion regarding the time and place to use one or the other.

The WCF Service Application Template

You create a WCF Service Application by way of Visual Studio's "Add New Project" dialog:

image

The end result is this:

image

It's no coincidence that the structure is similar to that of a web application. Specific to the WCF service, you have the service contract (IService1.cs), service implementation (Service1.svc.cs), and the web configuration file (web.config). In addition, we get a service host file (Service1.svc).

The WCF Service Library Template

Similar to the Service Application template, you create a WCF Service Library by way of Visual Studio's "Add New Project" dialog:

image

This produces a project tree as:

image

Much like the WCF Service Application project, a service library has a service contract (IService1.cs) and service implementation (Service1.cs). Instead of a web configuration file, though, we have an application configuration file (app.config). Also, no service hosting file.

With regard to the app.config file, MSDN states:

Visual Studio is configured to recognize the App.config file as the configuration file for the project when it is run using the WCF Service Host (WcfSvcHost.exe), which is the default configuration.

So what's the diff?

If you do a file comparison between like files you'll find the code is identical. Even the Service Application's web.config and Service Library's app.config are nearly the same (when looking at just the <system.serviceModel> sections). Also, both projects, when compiled, produce DLL's.

Perhaps the best way to illustrate the major difference is to run each project.

First, running the WCF Service Application give us the usual "Debugging Not Enabled" dialog just like any other web app:

image

Click-through that and you get the default directory listing:

image

Click on the Service1.svc hosting file to see the actual service:

image

As one might expect, the Service Application runs just like any other web app and is, in fact, hosted by default by the ASP.NET Development Server:

image

Side note: One bit of puzzlement came when I also saw the WCFSvcHost app come up when running the WCF Service Application (NOT the Service Library):

image

Turns out there is an option in the Service Library project that says:

image

Un-checking that box keeps WcfSvcHost from coming up when I'm running another application in the solution.

End side note

OK, now let's run the WCF Service Library and see what happens. It's hosted not by the development server, but by the WCF Test Client:

image

Why is this? Because the Service Library project does not, by default, have a service hosting file or a web.config. You can remedy this by adding each, but then you're essentially creating a WCF Service Application, right?

This gets us back to the original question, which I think can be answered inasmuch as the web hosting option is concerned with a definite "not much". Whether you choose Service Application or Library, you still need a .svc hosting file (you'll have to create one manually for the Service Library project), you still need to move the <system.serviceModel> config info from the WCF project to the web app config, and you still need either the codebehind file (from the Service Application) or the dll (from the Service Library).

The only reason I can see for going with a WCF Service Library is if you want to host the WCF service in something other than IIS, like, for example, a Windows service.

Conclusion

Hopefully I've illustrated the differences between a WCF Service Application and WCF Service Library, and you'll be able to make that first decision a little more easily now.

References

MSDN: WCF Visual Studio Templates


How to Install a WCF Service on a GoDaddy hosted site

June 24, 2009 16:26

Introduction

I've been engaged in the creation of a Silverlight TagCloud control of late. The architecture is such that I have the Silverlight control on the front-end, which communicates with a WCF service that in turn retrieves tag data from my blogging engine, BlogEngine.NET. I've completed development and now, as a first deployment step for the overall project, would like to deploy the WCF service to my site hosted by GoDaddy.com.

For purposes of this post I'm assuming you're at a similar point where you want to toss the web service over the wall and see what problems, if any, you run across in a "real" environment.

Deployment of the Service/The Problem

To that end, I identified the two files I needed to copy over to deploy the service: ~\TagCloudService.svc in my web site's root and ~\App_Code\TagCloudService.svc.cs, which was created for me in the App_Code folder when I right-clicked on my web site in VS2008, chose to add a new item, and selected "Silverlight-enabled WCF Service". (If you need some assistance getting this far, check out Step By Step - Using Silverlight to Access a WCF Service Hosted In a Console Application).

I FTP'ed the files to my site, fired up my browser, typed in the service's url, and promptly got this error:

This collection already contains an address with scheme http. There can be at most one address per scheme in this collection. Parameter name: item

In graphical format:

image

I wasn't expecting complete success right off the bat…

The Solution

So, I started digging, and almost immediately found this post (scroll down to the section sub-titled "WCF Services on web hosts with Multiple IIS Identities (GoDaddy)"), which provides a brief summation of the problem:

WCF services hosted on IIS can only have one base address. Under some hosting configurations, such as GoDaddy's shared Windows Economy Plan, multiple base addresses exist. When attempting to access WCF services on an IIS with multiple base addresses, an exception […] occurs.

That post coupled with this one proved to be of great help in coming up with the first solution to this problem. This one provided an alternative where you only need to modify the web.config file. I'll go over both approaches.

1.) Coding solution

For the code solution, I'm going to detail the very few steps I had to go through to get this to work in my situation. Your mileage may vary, so take a look at the other posts if what I go over here leaves you stuck as they detail some other things I didn't have to deal with.

First, open your .svc file. Add the "Factory" attribute to the ServiceHost declaration, replacing the web project name with your own:

 

   1: <%@ ServiceHost Language="C#" Debug="true" Service="TagCloud.Web.TagCloudService"
   2:     CodeBehind="~/App_Code/TagCloudService.svc.cs" Factory="TagCloud.Web.CustomHostFactory" %>

 

Now, in the service's code-behind file, add the following new class alongside your service class:

 

   1: class CustomHostFactory : ServiceHostFactory
   2: {
   3:     protected override ServiceHost CreateServiceHost (Type serviceType, Uri[] baseAddresses)
   4:     {
   5:         // If more than one base address exists then return the second address,
   6:         // otherwise return the first address
   7:         if (baseAddresses.Length > 1)
   8:         {
   9:             return new ServiceHost (serviceType, baseAddresses[1]);
  10:         }
  11:         else
  12:         {
  13:             return new ServiceHost (serviceType, baseAddresses[0]);
  14:         }
  15:     }
  16: }
  17:  
  18: class CustomHost : ServiceHost
  19: {
  20:     public CustomHost (Type serviceType, params Uri[] baseAddresses)
  21:         : base (serviceType, baseAddresses)
  22:     { }
  23: }

 

This has the effect of returning one and only one ServiceHost base address, thus satisfying IIS.

Re-deploy the changed files to your site, type in the web service url, and you should see confirmation that your WCF web service is good to go:

image

2.) Web.config solution

This one is especially useful if you start getting "404 – Resource not found" errors when trying to access the web service via the browser. I found I was getting this on a sub-site hosted on the server space of another GoDaddy web site of mine. I found that if I removed the "Factory" attribute from the ServiceHost declaration then I no longer got the "resource not found" error, but then I was back to square one on the original problem. So, modifying the web.config as follows was the solution to that particular issue.

Open your web app's web.config file and add the following to the system.servicemodel section:

   1: <serviceHostingEnvironment aspNetCompatibilityEnabled="true">
   2:     <baseAddressPrefixFilters>
   3:         <add prefix="http://www.itscodingtime.com"/>
   4:     </baseAddressPrefixFilters>
   5: </serviceHostingEnvironment>

Of course, change the add prefix to your own site's URL and you're done.

Conclusion

That's it. I know I didn't go into a lot of depth on why IIS only wants one base address and such, but there are plenty of other sources out there that can provide a better explanation than I can I've no doubt. I just want to get back to my code and have this process documented for the next time you or I run across it.

References

WCF Services on web hosts with Multiple IIS Identities (GoDaddy)

WCF: This collection already contains an address with scheme http

WCF error: "This collection already contains an address with scheme http"