In this blog post I complete the process of creating a custom WCF service host to allow IIS service hosting without a config file. In my prior post, I create mex endpoints in IIS without adding information in the web.config. The custom host created earlier will be extended in this post to create an endpoint using a WsHttpBinding. This approach has been tested only on IIS7 and .NET framework 3.5 SP1. The technique applies regardless of whether the ServiceContract attribute has been applied to an interface or a class.
First, lets examine the easier case of a class with the ServiceContract attribute applied. We start with the ABCs: address, binding, and contract. IIS (and the Cassini debugging host) provide the address and the class that implements the service to the ServiceHost constructor. This class is directly used as the contract. As in the prior post, I have modified the service .svc file to use the custom ServiceHostFactory we have created. The Custom ServiceHostFactory calls our custom ServiceHost, ZeroConfigServiceHost.
That was easy enough, but dealing with interfaces that have the ServiceContract attribute applied is trickier. The class implementing a given service is known but in order to provide the contract we need to determine the correct interface implemented by this class. I use reflection to identify this information.
I begin this identification by enumerating all interfaces in the current assembly to see if they have the ServiceContract attribute applied. I build a list containing all of these interface types. I then take the type of the class and find which ServiceContract it implements. I use the IsAssignableFrom method available to a System.Type object to match the class to its interface. Once the contract interface is identified it can be used to provide the contract for the endpoint. Everything else is the same as if the ServiceContract was directly applied to a class.
Source File 1: Reflection class
 Imports System.Reflection  
 Public Class ReflectionExperiment  
   Dim oServiceInterfaceTypes As New List(Of Type)  
   Public Sub New()  
     Dim oAssembly As Assembly  
     oAssembly = Assembly.GetExecutingAssembly()  
     oServiceInterfaceTypes = FindServiceInterfaces(oAssembly)  
   End Sub  
   Public Function FindServiceInterfaces(ByVal oAssembly As Assembly) As List(Of Type)  
     Dim ExportedTypes() As Type  
     Dim currType As Type  
     Dim olServiceInterfaceTypes As New List(Of Type)  
     ExportedTypes = oAssembly.GetExportedTypes()  
     For Each currType In ExportedTypes  
       If currType.IsInterface = True AndAlso IsTypeAServiceContract(currType) Then  
         olServiceInterfaceTypes.Add(currType)  
       End If  
     Next  
     Return olServiceInterfaceTypes  
   End Function  
   Public Function IsTypeAServiceContract(ByVal oType As Type) As Boolean  
     Dim oServiceContractAttribute As New System.ServiceModel.ServiceContractAttribute  
     Return Attribute.IsDefined(oType, oServiceContractAttribute.GetType())  
   End Function  
   Public Function FindServiceContractType(ByVal serviceType As Type, ByRef serviceContractType As Type) As Boolean  
     Dim bRetVal As Boolean = False  
     Dim currType As Type  
     If IsTypeAServiceContract(serviceType) Then  
       serviceContractType = serviceType  
       Return True  
     End If  
     For Each currType In oServiceInterfaceTypes  
       If currType.IsAssignableFrom(serviceType) Then  
         bRetVal = True  
         serviceContractType = currType  
         Exit For  
       End If  
     Next  
     Return bRetVal  
   End Function  
 End Class  
File 2: ServiceHost-related Classes
 Imports System.ServiceModel.Description  
 Imports System.ServiceModel.Activation  
 Public Class ZeroConfigServiceHost  
   Inherits ServiceHost  
   Private pReflectionExperiment As New ReflectionExperiment  
   Public Sub New()  
     MyBase.New()  
     AddMexEndpoint()  
   End Sub  
   Public Sub AddEndpoints (ByVal serviceType As Type, ByVal ParamArray baseAddresses As Uri())  
     Dim serviceContractType As Type  
     Dim oReflectionExperiment As New ReflectionExperiment  
     AddMexEndpoint()  
     If oReflectionExperiment.FindServiceContractType (serviceType, serviceContractType) Then  
       Me.AddServiceEndpoint (serviceContractType, New WSHttpBinding, baseAddresses (0).AbsoluteUri)  
     End If  
   End Sub  
   Public Sub AddMexEndpoint()  
     Dim mb As ServiceMetadataBehavior  
     mb = Me.Description.Behaviors.Find (Of ServiceMetadataBehavior)()  
     If (mb Is Nothing) Then  
       mb = New ServiceMetadataBehavior()  
       mb.HttpGetEnabled = True  
       Me.Description.Behaviors.Add (mb)  
       Me.AddServiceEndpoint (ServiceMetadataBehavior.MexContractName, _  
                   MetadataExchangeBindings.CreateMexHttpBinding(), _  
                   "mex")  
     End If  
   End Sub  
   Public Sub New (ByVal serviceType As Type, ByVal ParamArray baseAddresses As Uri())  
     MyBase.New (serviceType, baseAddresses)  
     AddEndpoints (serviceType, baseAddresses)  
   End Sub  
   Public Sub New (ByVal singletonInstance As Object, ByVal ParamArray baseAddresses As Uri())  
     MyBase.New (singletonInstance, baseAddresses)  
     AddMexEndpoint()  
   End Sub  
 End Class  
 Public NotInheritable Class ZeroConfigServiceHostFactory  
   Inherits ServiceHostFactory  
   Public Overrides Function CreateServiceHost (ByVal constructorString As String, ByVal baseAddresses As Uri()) _  
     As ServiceHostBase  
     Return MyBase.CreateServiceHost (constructorString, baseAddresses)  
   End Function  
   Protected Overrides Function CreateServiceHost (ByVal serviceType As Type, ByVal baseAddresses As Uri()) _  
     As ServiceHost  
     Return New ZeroConfigServiceHost (serviceType, baseAddresses)  
   End Function  
 End Class  
 
No comments:
Post a Comment