Monday, February 22, 2010

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

WCF configuration is, in my mind, the biggest misstep made within WCF (prior to .NET 4.0), compared to ASMX web services. For someone getting starting with web services, their first steps should “just work”. My early successes with ASMX web services allowed me to build confidence. WCF, on the other hand, forces one to immediately deal with System.ServiceModel XML configuration hell. This can scare developers away, especially with the incredibly configurable and extensible WCF. Fortunately .NET 4.0 does allow zero-config WCF, but what about .NET 3.5? This post shows part of how to accomplish this within ASP.NET.



I don’t want to advocate config-less use of WCF in production, but it is sure convenient to have this available in testing. When I first worked with WCF, I was not aware of the WCF Service Configuration Tool (SvcConfigEditor.exe) which has a GUI to more safely edit the System.ServiceModel part of a config file. This caused a lot of teeth-gnashing indeed.


The first big step to making WCF just work was POCO (plain old CLR object) support added to .NET 3.5 SP1. This brings back the ASMX default serialization of including all public fields and properties in a class for serialization. In WCF before 3.5 SP1, a class would have the DataContract attribute applied to it.


In this post I explain how to use a custom ServiceHost and ServiceHostFactory to automatically add HTTP-based metadata exchange (MEX) support for any given service hosted within ASP.NET. This code is reuseable, requiring only the Factory attribute to be added to the service directive line in the .svc. The .svc Factory refers to a custom service host factory. The custom factory simply has code to reference the custom ServiceHost.


The ServiceHost class inherits from the default ServiceHost class. It calls the base class for all functionality and then adds the mex-specific logic. Identifying which extensibility hook is needed is the hard part. From there it simply a matter of mirroring the config file entries to enable mex with programmatic setup. As with the config file, a behavior must be added to set HttpGetEnabled = “true” and add a Mex Endpoint to the service. Note that this example covers HTTP based metadata exchange, not HTTPS, although the concepts are similar.



Step 1: Add the ServiceHost and ServiceHostFactory to a new file within your web site.
 Imports System.Collections  
Imports System.Collections.Generic
Imports System.ServiceModel.Channels
Imports System.ServiceModel.Description
Imports System.ServiceModel.Dispatcher
Imports System.Xml.Schema
Imports ServiceDescription = System.Web.Services.Description.ServiceDescription
Imports System.ServiceModel.Activation
Public Class AutoMexServiceHost
Inherits ServiceHost
Public Sub New()
ConditionallyAddMexEndpoint()
End Sub
Public Sub ConditionallyAddMexEndpoint()
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)
ConditionallyAddMexEndpoint()
End Sub
Public Sub New(ByVal singletonInstance As Object, ByVal ParamArray baseAddresses As Uri())
MyBase.New(singletonInstance, baseAddresses)
ConditionallyAddMexEndpoint()
End Sub
End Class
Public NotInheritable Class AutoMexServiceHostFactory
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 AutoMexServiceHost(serviceType, baseAddresses)
End Function
End Class


Step 2: Add Factory to your existing .svc file
For example,


<%@ ServiceHost Language="VB" Debug="true" Service="WcfNoodler.Service1" CodeBehind="Service1.svc.vb" Factory="WcfNoodler.AutoMexServiceHostFactory"%>

2 comments:

  1. You got some "heat" on Stack Overflow for posting the link to this series. I for one want to thank you for posting that link.

    I've been programming ASP.NET using WDSL and was recently asked to write a WCF app.
    Needless to the configuration file is the biggest bottleneck because -- you cannot debug them. One mistake -- forget it. The WDSL tools worked and they generated the xml files on the fly. WCF demand that you work with them and that can only lead to more errors.

    I really don't see the need for them as once you set up the endpoints they are not like to change. What should be in the configuration files are addresses. This notion of putting everything in xml files is NOT programming.

    Programming is what enables you to get deeper with operation of the system and enable you to get a better understanding.

    Thus I've been looking for examples that demonstrate how this can be done. Just telling people to buy a book (which was the Stack Overflow answer) is NO answer.

    Thanks again for posting a link to a real example.

    ReplyDelete
  2. Hi John,

    Can you please upload a complete sample project for the article?

    Best Regards,
    Leo Ge

    ReplyDelete