Thursday, February 25, 2010

WCF Zero-Config in .NET 3.5! (Part II)

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