CQ-CSER

计算机爱好者

wcf学习笔记2

Posted on | 三月 16, 2010 | No Comments

1.5. Hosting

The WCF service class cannot exist in a void. Every WCF service must be hosted in a Windows process called the host process. A single host process can host multiple services, and the same service type can be hosted in multiple host processes. WCF makes no demand on whether or not the host process is also the client process, although having a separate process obviously promotes fault and security isolation. It is also immaterial who provides the process and what kind of process is involved. The host can be provided by Internet Information Services (IIS), by the Windows Activation Service (WAS) on Windows Vista or Windows Server 2008 or later, or by the developer as part of the application.

.5.1. IIS 5/6 Hosting

The main advantage of hosting a service in the Microsoft IIS web server is that the host process is launched automatically upon the first client request, and you rely on IIS 5/6 to manage the lifecycle of the host process. The main disadvantage of IIS 5/6 hosting is that you can only use HTTP. With IIS 5, you are further restricted to having all services use the same port number.

Hosting in IIS is very similar to hosting a classic ASMX web service. You need to create a virtual directory under IIS and supply an .svc file. The .svc file functions similarly to an .asmx file and is used to identify the service code behind the file and class. Example 1-2 shows the syntax for the .svc file.

Example 1-2. A .svc file
<%@ ServiceHost
   Language   = "C#"
   Debug      = "true"
   CodeBehind = "/App_Code/MyService.cs"
   Service    = "MyService"
%>

//.svc文件

1.5.2.1. Using Visual Studio 2008

Visual Studio 2008 allows you to add a WCF service to any application project by selecting WCF Service from the Add New Item dialog box. A service added this way is, of course, in-proc toward the host process, but out-of-proc clients can also access it.

1.5.2.2. Self-hosting and base addresses

You can launch a service host without providing any base address by omitting the base addresses altogether:

ServiceHost host = new ServiceHost(typeof(MyService));
You can also register multiple base addresses separated by commas, as in the following snippet, as long as the addresses do not use the same transport schema (note the use of the params qualifier in Example 1-3):
Uri tcpBaseAddress  = new Uri("net.tcp://localhost:8001/");
Uri httpBaseAddress = new Uri("http://localhost:8002/");

ServiceHost host = new ServiceHost(typeof(MyService),
                                   tcpBaseAddress,httpBaseAddress);

WCF also lets you list the base addresses in the host config file:

<system.serviceModel>
   <services>
      <service name = "MyNamespace.MyService">
         <host>
            <baseAddresses>
               <add baseAddress = "net.tcp://localhost:8001/"/>
               <add baseAddress = "http://localhost:8002/"/>
            </baseAddresses>
         </host>
         ...
      </service>
   </services>
</system.serviceModel>

When you create the host, it will use whichever base addresses it finds in the config file, plus any base addresses you provide programmatically. Take extra care to ensure that the configured base addresses and the programmatic ones do not overlap in the schema.

You can even register multiple hosts for the same type, as long as the hosts use different base addresses:

Uri baseAddress1  = new Uri("net.tcp://localhost:8001/");
ServiceHost host1 = new ServiceHost(typeof(MyService),baseAddress1);
host1.Open(  );

Uri baseAddress2  = new Uri("net.tcp://localhost:8002/");
ServiceHost host2 = new ServiceHost(typeof(MyService),baseAddress2);
host2.Open(  );

However, with the exception of some threading issues discussed in Chapter 8, opening multiple hosts this way offers no real advantage. In addition, opening multiple hosts for the same type does not work with base addresses supplied in the config file and requires use of the ServiceHost constructor.

1.5.2.3. Advanced hosting features

The ICommunicationObject interface supported by ServiceHost offers some advanced features, listed in Example 1-4.

Example 1-4. The ICommunicationObject interface
public interface ICommunicationObject
{
   void Open(  );
   void Close(  );
   void Abort(  );

   event EventHandler Closed;
   event EventHandler Closing;
   event EventHandler Faulted;
   event EventHandler Opened;
   event EventHandler Opening;

   IAsyncResult BeginClose(AsyncCallback callback,object state);
   IAsyncResult BeginOpen(AsyncCallback callback,object state);
   void EndClose(IAsyncResult result);
   void EndOpen(IAsyncResult result);

   CommunicationState State
   {get;}
  //More members
}
public enum CommunicationState
{
   Created,
   Opening,
   Opened,
   Closing,
   Closed,
   Faulted
}

If opening or closing the host is a lengthy operation, you can do so asynchronously with the BeginOpen( ) and BeginClose( ) methods. You can subscribe to hosting events such as state changes or faults, and you can use the State property to query for the host status. Finally, the ServiceHost class also offers the Abort( ) method. Abort( ) is an ungraceful exit—when called, it immediately aborts all service calls in progress and shuts down the host. Active clients will each get an exception.

1.5.2.4. The ServiceHost<T> class

You can improve on the WCF-provided ServiceHost class by defining the ServiceHost<T> class, as shown in Example 1-5.

Example 1-5. The ServiceHost<T> class
public class ServiceHost<T> : ServiceHost
{
   public ServiceHost(  ) : base(typeof(T))
   {}
   public ServiceHost(params string[] baseAddresses) :
                                           base(typeof(T),Convert(baseAddresses))
   {}
   public ServiceHost(params Uri[] baseAddresses) : base(typeof(T),baseAddresses)
   {}
   static Uri[] Convert(string[] baseAddresses)
   {
      Converter<string,Uri> convert = (address)=>
                                      {
                                         return new Uri(address);
                                      };
      return baseAddresses.ConvertAll(convert);
   }
}

ServiceHost<T> provides simple constructors that do not require the service type as a construction parameter and that can operate on raw strings instead of the cumbersome Uri. I'll add quite a few extensions, features, and capabilities to ServiceHost<T> in the rest of this book.

Array and Iterator Extensions

It would be nice if the iterator (any collection or array) supported a method that converted it to another collection or an array, such as the ConvertAll( ) call in Example 1-5. Sadly, .NET 3.5 does not provide such a method. However, using C# 3.0 extension methods, you can add methods to existing types to compensate for this oversight:

public static class CollectionExtensions
{
   public static IEnumerable<U> ConvertAll<T,U>(
                            this IEnumerable<T> collection,
                            Converter<T,U> converter)
   {
      foreach(T item in collection)
      {
         yield return converter(item);
      }
   }
   //More extensions
}
public static class ArrayExtensions
{
   public static U[] ConvertAll<T,U>(this T[] array,
                                  Converter<T,U> converter)
   {
      IEnumerable<T> enumerable = array;
      return enumerable.ConvertAll(converter).ToArray(  );
   }
   //More extensions
}

CollectionExtensions first extends the IEnumerable<T> interface that all collections and arrays support to include a ConvertAll( ) method using the this keyword. CollectionExtensions.ConvertAll( ) uses C# iterators to convert the collection into another collection by invoking the supplied Converter<T,U> delegate on every item in the collection. When used on an array (that supports IEnumerable<T>), it will convert an array of T into an IEnumerable<U>. To return a proper U[], define the ArrayExtensions class with its own ConvertAll( ) method. ArrayExtensions.ConvertAll( ) calls CollectionExtensions.ConvertAll( ) to convert the array into IEnumerable<U>, followed by a call to the LINQ method ToArray( ) to convert the iterator into an array. These extension methods are part of my ServiceModelEx library, which includes many other type extensions for the iterator and the array (such as ForEach( )). In addition, I have provided all of the LINQ functionality that normally returns an IEnumerable<T> interface as array extensions that return a proper array of T.

1.5.3. WAS Hosting

The Windows Activation Service (WAS) is a system service available with Windows Vista and Windows Server 2008 (or later). WAS is part of IIS7, but it can be installed and configured separately. To use WAS for hosting your WCF service, you need to supply a .svc file, just as with IIS 5/6. All the other development aspects, such as support in Visual Studio 2008, remain exactly the same. The main difference between IIS and WAS is that WAS is not limited to HTTP and can be used with any of the available WCF transports, ports, and queues.

WAS offers many advantages over self-hosting, including application pooling, recycling, idle time management, identity management, and isolation, and it is the host process of choice when available—that is, when you can target either a Windows Server 2008 (or later) machine for scalability, or a Windows Vista (or later) client machine for a handful of clients.

That said, self-hosted processes do offer singular advantages, such as in-proc hosting, dealing well with unknown customer environments, and easy programmatic access to the advanced hosting features described previously. In addition, in some instance-management cases (such as lengthy sessions or singleton services) it is better to use self-hosting so that you can avoid WAS autorecycling of the host process and control when to launch the host.

1.5.4. Custom Hosting in IIS/WAS

It is often the case that you need to interact with the host instance. While this is integral to the use of a self-hosting solution, when using IIS 5/6 or WAS, you have no direct access to the host. To overcome this hurdle, WCF provides a hook called a host factory. Using the Factory tag in the .svc file, you can specify a class you provide that creates the host instance:

<%@ ServiceHost
   Language   = "C#"
   Debug      = "true"
   CodeBehind = "/App_Code/MyService.cs"
   Service    = "MyService"
   Factory    = "MyServiceFactory"
%>

The host factory class must derive from the ServiceHostFactory class and override the CreateServiceHost( ) virtual method:

public class ServiceHostFactory : ...
{
   protected virtual ServiceHost CreateServiceHost(Type serviceType,
                                                   Uri[] baseAddresses);
   //More members
}

For example:

class MyServiceFactory : ServiceHostFactory
{
   protected override ServiceHost CreateServiceHost(Type serviceType,
                                                    Uri[] baseAddresses)
   {
      ServiceHost host = new ServiceHost(serviceType,baseAddresses);

      //Custom steps here

      return host;
   }
}

相关文章:

  1. WCF学习笔记1
  2. wCF笔记3

评论|Comments

留言|Leave a Reply





  • Archives

  • SUNSHINE

  • About

    本博客采用创作共用版权协议,要求署名、非商业用途和保持一致. 转载本博客内容也遵循“署名-非商业用途-保持一致”的创作共用协议.

    订阅

    Search

    Admin