Hosting WCF Services (cont'd)
Self-Hosting
Self-hosting is the simplest way to host your services-and the approach that yields the least number of hosting features. As the label implies, self-hosting requires you to write the code necessary to initialize the ServiceHost and manage its lifetime. At a minimum you provide a managed process, instantiate a ServiceHost for each service, and then initialize and open them to provide a communication channel for each endpoint to receive incoming messages.
advertisement


"Windows services are the most useful of the self-hosting environments for WCF services. In fact, for services deployed in a production server system, Windows services are the only practical self-hosting choice…"
Typically you'll keep ServiceHost instances alive for the lifetime of the application in which they are hosted. Once you open the ServiceHost, the service model allocates a worker thread to process each incoming request to its respective service endpoints. Your job is to keep the application alive as long as you want to service those requests. Any managed application process will suffice including console, Windows Forms, WPF, or managed Windows services as I discussed earlier.

In this section, I'll walk you through the relevance of these self-hosting environments including scenarios where they are most applicable.

Console Applications
Console applications are a popular hosting environment for developing and testing services. As I discussed earlier, you need only create and open the ServiceHost instance and keep the console process alive to receive and process requests. Listing 1 illustrates this.

A console application is ultimately impractical for deploying production services for a few reasons:

  • A user must be logged in to the machine to start the process.
  • The user console can be easily closed—taking the communication channel along with it.
Still, you can expect to see plenty of console hosts in sample code you download for WCF, and you will likely use them to execute rudimentary tests on your services.

 
Figure 2: A Windows client hosting in-process WCF services for offline work, and consuming remote WCF services over the Web while online.
Windows Applications
Both Windows Forms and WPF applications can host WCF services associated with an application interface. These scenarios might warrant this arrangement:

  • Chat applications that participate in a peer mesh will expose services to receive broadcast messages.
  • Client applications may host services in-process to consume local services without crossing a process boundary. For example, a smart client application may use local services when operating offline and remote services while online (see Figure 2).
  • Services deployed to client or server machines may have an associated user interface if the service presents an activity log or provides some administrative controls for service activation and deactivation.
Unlike console hosting, you can program a Windows application to avoid accidental shutdown with dialog box interaction. However, these applications do require a user to be logged in to start the application—and this limits their usefulness in server deployments. Managed Windows services solve this problem, and may sometimes incorporate a user interface, so it is still important to understand the concurrency implications of hosting services when UI threads are involved.

A simple way to host services in a Windows application is to process requests on the UI thread. This means that the application processes requests through the Windows message loop, one at a time. In fact, this is the default behavior if you construct the ServiceHost instance while running on the UI thread. For example, the following code illustrates constructing the ServiceHost in the Form constructor for a Windows Forms application:

   public partial class Form1 : Form
   {
     public Form1()
     {
       InitializeComponent();
       localhost.HelloIndigoServiceClient proxy = new 
   localhost.HelloIndigoServiceClient();
     }
   // other code
   }
You can verify that the service is running on the UI thread by checking the Application.MessageLoop property:

   Debug.Assert(Application.MessageLoop);
While running on the message loop, service operations can freely interact with the user interface, setting control and Form properties and so forth. The downside is that the message loop acts as a throttle for message processing—even if the service were to allow multiple concurrent requests.

From the same Windows Form application if you were to construct the ServiceHost instance before starting the UI thread, it will run on its own thread. That means worker threads allocated from the thread pool process messages instead of the message loop. Thus, services can truly process multiple concurrent requests.

One way to achieve this is to construct the ServiceHost during the Main() entry point for the Windows application, before invoking Application.Run() as shown in the code below.

   [STAThread]
   static void Main()
   {
      Application.ApplicationExit += new EventHandler(
         Application_ApplicationExit);
      Program.MyServiceHost = new ServiceHost(
         typeof(HelloIndigo.HelloIndigoService));
      Program.MyServiceHost.Open();
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
   }
   static void Application_ApplicationExit(
      object sender, EventArgs e)
   {
      if (Program.MyServiceHost!=null)
      {
         Program.MyServiceHost.Close();
         Program.MyServiceHost=null;
      }
   }
You can see in the preceding code that the application also handles the Application.Exit event to properly dispose of the ServiceHost instance. The lifetime of the ServiceHost in this example is the duration of the application, not tied to a particular Form instance. Closing the ServiceHost disposes the ServiceHost instance along with the thread in which it was executed. In that case the application must recreate and open the ServiceHost in order to receive subsequent requests.

Processing requests on the UI thread is not practical for services that require decent throughput on the server. In fact, it is unlikely that a service that processes significant requests will even have a UI associated with it. However, assuming you did want to attempt throughput and UI, you can configure your services to run on a separate thread by setting the UseSynchronizationContext property to false for the ServiceBehaviorAttribute.

   [ServiceBehavior(UseSynchronizationContext=false)]
   public class HelloIndigoService : 
   IHelloIndigoService
   {
   // service implementation
   }
The default value for this property is true, which means that services will join the UI thread if the ServiceHost is constructed on that thread. By setting this attribute to false your service will always process requests on worker threads from the thread pool—regardless of where the application constructs the ServiceHost.

Accepting requests on multiple threads introduces the potential for concurrency issues when service operations or downstream code must interact with UI or shared resources. Since the UI will always be running on another thread, you can't directly interact with control or Form properties. Each control (including the Form) has an InvokeRequired property that is set to true when the current thread is not the same as the UI thread that owns the control. When true, you can use the same control's Invoke() method to invoke any members exposed by the control.

Listing 2 shows a service implementation that posts messages to a Windows Form when you call its SendMessage() operation. In this case, the service is coupled to the user interface, so the static constructor of the service type creates the Form and shares it between all running instances of the service. If you close or dispose of the Form each service instance recreates the Form on demand. Although not shown in this reduced listing (the code sample has a complete implementation) the service type also provides a public ShowForm() method so that the host application can control the Form's visibility.

When you call SendMessage(), it is running on a separate thread from the Form. The Form exposes a public method, AddMessage(), which encapsulates the check for InvokeRequired before adding the string to the ListBox control:

   public void AddMessage(string message)
   {
   if (this.InvokeRequired)
     {
       MethodInvoker del = delegate 
         {
   this.listBox1.Items.Add(message);
         };
   this.Invoke(del);
     }
   else
   this.listBox1.Items.Add(message);
   }
In a multithreaded environment, you must write code like this for all communication with control and Form properties and methods. You can simplify this by encapsulating the functionality in the Form or in custom thread-safe controls.

Aside from Form and control properties, you must also protect other shared resources when you allow multiple threads into the service. For example, Listing 2 illustrates the use of the lock() statement to protect access to the shared m_form reference. This statement applies a global lock to the Form instance such that only one thread at a time can be interacting with any of its properties or methods. There are other advanced synchronization techniques to increase throughput, such as Mutex, Semaphore, and other WaitHandle types from the System.Threading.WaitHandle namespace.

Aside from throughput considerations that drive your decision to host services on the UI thread, or provide multithreading support and manage concurrency, you should consider the following guidelines for Windows application hosting:

  • Decouple service and host implementation where possible, so that the service code is not tied to a single host implementation.
  • Where possible, the host should only provide UI related to hosting-that is, UI to control the ServiceHost instance-not UI specific to the service functionality. Avoid having services talk directly to host process UI.
  • If the service is associated with UI, encapsulate the creation and display of this UI in the service assembly, not the host. This reduces coupling between service and host. If applicable, services can provide a mechanism for hosts to interact with service UI.
Previous Page: Associating with a ServiceHost Next Page: Managed Windows Services
Page 1: IntroductionPage 4: Managed Windows Services
Page 2: Associating with a ServiceHostPage 5: Message-Based Activation
Page 3: Self-HostingPage 6: IIS 7.0 and WAS Hosting