|
||||||||||||||
|
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.
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:
Both Windows Forms and WPF applications can host WCF services associated with an application interface. These scenarios might warrant this arrangement:
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:
You can verify that the service is running on the UI thread by checking the Application.MessageLoop property:
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 processingeven 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.
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.
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 poolregardless 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:
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:
|
||||||||||||||
|