Friday, November 28, 2008

Hosting an “Indigo” (WCF) Service


Table of Contents:

Introduction

Hosting Environments

· ASMX Services

· WCF Services

· Different Hosting Environments

WAS Hosting Environment

· A Simple Hosted Service

· Service Configuration

· Service Addressing

· Service Activation

· Service Recycling

Let me start with something simple for this topic. I first make a few assumptions here:

  • You have used Microsoft Internet Information Services (IIS).
  • You are familiar with .NET Programming with Common Language Runtime (CLR) and ASP.NET.
  • You have heard of Microsoft Windows Communications Foundation (WCF), code-named “Indigo”.

That is all what you need to know before reading the following!

Below I will write about how to host a WCF service in a special “hosting” environment.

Introduction

There are tons of articles and blogs out there talking about Service Oriented Architecture (SOA), Web Services, and WCF. Never mind if you have not read them. So I will make things as simple as possible. Basically, WCF, as the next generation of service-oriented programming platform, provides a set of .NET APIs in its Version 1 (V1) for developers to use. It will be released as part of a larger package WinFX. You can download the most recent PDC release fromhere.

Every service-oriented programming model is also a client-server model. WCF has no difference. Both the client and service counterparts of WCF are based on the so-called “ABC” model: Address, Binding, and Contract. People also call this as “WHW” model: Address stands for “Where”, Binding stands for “How”, and Contract stands for “What”. Among the three, Binding is the most complicated part in this model. It tells WCF how to transfer data between the client and the service:

  • What transport do we use: HTTP, TCP, MSMQ, Named Pipe, etc?
  • What encodings do we use for the data to be encoded?
  • What security mechanisms do we use for the underlying channels?
  • Etc.

For each WCF service, there is a host that manages service instances and lifetime. The base type of the host is System.ServiceModel.ServiceHost. It provides events and extensibility points for you to programming with. Actually I am lying here. ServiceHost is not the most basic service host type, but System.ServiceModel.ServiceHostBase is. However, you will never touch this base type if you do not want to re-create the whole channel stack to replace WCF underlying implementations.

How far have I diverted from my topic? Aren’t we talking about WCF service hosting? No, not yet! The concept of “host” has relative meanings in different programming plug-in layers. The “host” that I will talk about here has a broader meaning: it is application-level “host”. That is, a host is an application that loads one or more WCF ServiceHosts, which we will call services in the following, and manages their lifetime. In some cases, it provides an environment that works as the entry point for all messages that are routed to the services. Am I clear here? If not, please read on.

Hosting Environments

There are different types of hosting environments for WCF services. To understand how these hosting environments work, we need to go back to see how the predecessor of WCF, a.k.a., ASMX works.

ASMX Services

When ASP.NET V1 was shipped, a Web service framework known as ASMX was introduced. It is tightly integrated with the ASP.NET HTTP pipeline. This pipeline provides the hosting environment to ASMX services and it is almost always hosted inside ASP.NET applications which are managed by IIS. In this way, ASMX services can leverage the advantages of ASP.NET applications such as process management, application recycling, configuration and deployment, and extensibility points provided by ASP.NET HTTP pipeline.

It is not required to have IIS to run ASMX services. For example, you can use the ASP.NET Cassini sample web server to host an ASP.NET application and thus ASMX services. You also build a web server into your own application to host the ASP.NET pipeline using the managed HTTP.sys utility System.Net.HttpListener(see Run ASMX without IIS).

However, ASMX services cannot run without the ASP.NET HTTP pipeline. Even though you can integrate the pipeline into your application as above, you still need to think of the ASMX services as being in a different world, that is, they have their own configuration and deployment which are quite web-flavored. In this sense, WCF services go one big step beyond to make service-oriented programming real.

WCF Services

The fact that ASMX services are tightly coupled with ASP.NET HTTP pipeline is due to the flat and passive HTTP request handling mechanism. HTTP requests received from IIS are passed on to ASP.NET pipeline and then are consumed by service methods.

WCF services, however, are based on a strictly layered model to break this paradigm. Roughly speaking, there are three different layers for WCF services:

  • ServiceModel layer
  • Channel layer
  • Transport layer

This layered model is similar as the Open System Interconnection (OSI) model. The lowest Transport layer is responsible for listening and receiving requests and converting the received data into “Messages”. A Message is a cross layer data packet that travels all of the layers to the top. The Channel layer provides a Message processing framework for different service requirements. In the real implementation, it actually consists of one or more sub-channel layers such as Basic layer, Security layer, ReliableMessaging layer, Transaction layer, etc. The top ServiceModel layer is the application layer that manages service instances and invokes service operations. It provides Object Models (OM) for people to program with.

In this layered model of WCF services, the top SericeModel layer is also responsible for building the lower level layers in different hosting environments. It also makes the Transport layer to be freely chosen among different communication protocols. This is why it does not rely on the ASP.NET HTTP pipeline as ASMX services does.

Different Hosting Environments

As mentioned above, WCF services are hosted by a ServiceHost. Ideally, any application or environment can create ServiceHost to host WCF services. In reality, we classify the different environments into three categories:

  • Windows Activation Service (WAS) hosting environment
  • EXE applications
  • NT Services

The WAS hosting environment is a broad concept that extends the ASP.NET HTTP pipeline hosting concept for ASMX services. WAS is an NT service inWindows Vista. It is separated out from the legacy IIS as a standalone Windows component that provides protocol-agnostic activation mechanism for different protocols besides HTTP. The IIS, Cassini, and any custom Web Server flavored hosting environments also fall into this category since they share the same WCF service deployment and activation mechanism. I will talk about this more in later sections.

You can easily host a WCF service in an EXE application so that you can control the lifetime of the ServiceHost. Furthermore, the WCF service shares the same configuration file with the application. This hosting environment is mostly useful when you also build your client code into the application. A good example is that a sophisticated business application with 3-tier architecture can have WCF services to pass messages between different layers.

NT Services are special EXE applications which are long running and the lifetime is controlled by Service Control Manager (SCM). Because of the long running behavior, you need to be careful of all of different requirements for writing an NT Service: security, reliability, and performance. You can use Microsoft Visual Studio to create managed NT Services with “Windows Service” project type.

WAS Hosting Environment

As mentioned above, a WCF service works similarly as an ASMX Service. For HTTP protocol, it relies on ASP.NET HTTP pipeline to transfer data. For non-HTTP protocols such as Net.Tcp, Net.Pipe, and Net.Msmq etc, it relies on the extensibility points, i.e., ProcessProtocolHandler (PPH) and AppDomainProtocolHandler (ADPH), of ASP.NET to transfer data. We will use the term “protocol handler” interchangeably to refer to either the ASP.NET HTTP pipeline or the ADPH. In the context of this writing, WCF services hosted in this environment are called WAS hosted services or simply called hosted services, and in contrast, services hosted in NT Services or EXE applications are calledself-hosted services.

Let’s first take a look at a simple hosted service and see how it works.

A Simple Hosted Service

A hosted service requires a physical service file with extension “.svc” to be associated with it. Here is the content of a simple service file HelloWorld.svc:

<%@Service Language="C#" Class="HelloWorld.HelloService" %>

using System;

using System.ServiceModel;

namespace HelloWorld

{

[ServiceContract]

public interface IHelloContract

{

[OperationContract]

string Hello(string greeting);

}

[ServiceBehavior]

class HelloService : IHelloContract

{

public string Hello(string greeting)

{

return "You said: " + greeting;

}

}

}

This file has a Service directive which is enclosed in the “<% %>” block and it also contains inline C# code. The Service directive tells the hosting environment which Service this file points to.

Just as ASMX services, the code can be compiled into a DLL that is deployed to the Global Assembly Cache (GAC), or to the application’s “\bin” directory, or it can be put in a C# file under the application’s “\App_Code” directory.

With this service file, we have defined a service contract IHelloContract and the service implementation. However, the service still does not have an endpoint defined yet. You can either define an endpoint declaratively through the configuration file “web.config” or imperatively through code. The latter will be covered separately in a different blog later.

Service Configuration

The service configuration inside the web.config file is very similar as that of self-hosted services. Here is an example of web.config file for the above service:

xml version="1.0" encoding="utf-8"?>

<configuration>

<system.serviceModel>

<services>

<service type="HelloWorld.HelloService">

<endpoint binding="basicHttpBinding"

contract="HelloWorld.IHelloContract" />

service>

services>

system.serviceModel>

configuration>

I need to point out a few points for this config file:

1) Service Type Attribute: The service type “HelloWorld.HelloService” specified here plays as a lookup key for the corresponding HelloWorld.svc. It tells the hosting environment which service will take the configuration specified here.

2) Endpoint Address: I did not specify the “address” of the endpoint here. The endpoint address is the same as the base address in this case. I will expand this in the next section.

3) Nested web.config: The service configuration can be specified in nested web.config files. For ASP.NET applications, a web.config file can locate in any sub-directories or virtual web directories as well as the root of the virtual application. All of the settings in the nested web.config files are merged for the virtual ancestors of the directory where the .svc file locates.

Service Addressing

The .svc file does not contain much useful information except for providing the service type which is also specified in the web.config file. Then why do we need .svc files after all? The main reason is that we need them to provide intuitive addressing model.

Suppose that we drop the above HelloWorld.svc file into a virtual application “/test”, we automatically have the following base address for the service:

http://localhost/test/HelloWorld.svc

You can enable different protocols for a single ASP.NET application. Each protocol needs to have a protocol binding for the whole web site. For IIS6 and below, the only protocol that is supported is HTTP (which implies HTTPS).

The WAS hosting environment looks up all enabled protocols for the ASP.NET application and generates the base addresses for the hosted service. Each base address has the following format:

://[:]//.svc

You can access the base addresses through the ServiceHostBase.BaseAddresses property. In WCF V1, only one base address is supported for each protocol. So if there are two or more bindings for HTTP is specified, you will get an ArgumentException on the service side with error message “Collection already contains an address with scheme http”.

Note: It is NOT recommended to specify an absolute address (with protocol scheme included) to the endpoint in web.config, otherwise, you will get AddressAccessDeniedException on the service side if the address does not match the service base address up to the application path () or EndpiontNotFoundException if you happen to match the application path but not the service path.

Service Activation

The WAS hosting environment is represented by the public type System.ServiceModel.ServiceHostingEnvironment. It is responsible for service activation, that is,

· It finds the service compilation information from System.Web.Compilation.BuildManager

· It creates the ServiceHost instance to host the service with compilation information.

· It then calls ServiceHost.Open to build the layers.

· It then caches the service and manages its lifetime inside the AppDomain.

This whole process is exposed through the following public method:

public static void EnsureServiceAvailable(stringvirtualPath)

This API is protocol agnostic. It is an extensible point for a custom protocol handler to activate the service.

The service activation process has the following characteristics:

1) Activation on demand: A WCF service is activated in the AppDomain only if EnsureServiceAvailable is called by one protocol handler for that service. Once it is activated, it will be active in the AppDomain until the AppDomain is unloaded.

2) Activation once for all: A WCF service can be activated from any protocol handler. Once it is activated, it is activated for all endpoints and thus all protocols. So when a second protocol handler calls EnsureServiceAvailable, it is a no-op as the service has been activated.

Service Recycling

Hosted WCF services enjoy all of the features for ASP.NET applications. One of the major feature is application recycling including AppDomain recycling and process (or AppPool) recycling. There are different factors that can cause application recycling to happen. See “Recycling Application Pool Settings” for more information about process recycling. When the worker process is recycled, all of the AppDomains in that process are also recycled. The configuration setting HostingEnvironmentSection.IdleTimeout can control AppDomain recycling. Also an AppDomain can be recycled if critical files for the application are changed. These files include web.config, assemblies in the “\bin” directory, code files in the “\App_Code” directory, etc.

Whenever a .svc file is modified in application, the AppDomain is also recycled. This is to simplify the service recycling mechanism. When the AppDomain is recycled, the ServiceHostingEnvironment tries to close all of the cached WCF services in a timely manner. Services not closed timely are aborted at the end. The timeout is controlled by the HostingEnvironmentSection.ShutdownTimeoutproperty.

The drawback of service recycling is that all of the sessionful data is lost. This means that Security and ReliableMessaging break when recycling happens. The workaround is to leverage the ASP.NET state service features by enabling AspNetCompatibility mode. I will expand on this in a later blog.

ASP.NET Async Pages vs Async WCF Service Operation

In Dmitry’s blog, he mentioned about how to write ASP.NET async pages with ASP.NET 2.0. Basically, the server page can handle the request asynchronously without blocking the request thread and the server thread can be returned to the thread pool to handle other requests.

WCF is quite flexible in supporting asynchronous programming. Actually the client and the service can use any pattern (sync or asnc) in their own way to perform the same operation. For example, you can use the following service contract with an async operation in it:

[ServiceContract(Namespace = "http://tempuri.org", Name = " HelloService")]

interface IHelloService

{

[OperationContract(IsOneWay = false, AsyncPattern = true)]

IAsyncResult BeginHello(string text, AsyncCallbackcallback, object state);

string EndHello(IAsyncResult result);

}

This allows you to implement the service operation in the same way as ASP.NET async pages.

On the client side, you can access the service either from an async call or a sync call. For example, you can have a sync client to access the above async service operation with the following client contract:

[ServiceContract(Namespace = "http://tempuri.org", Name ="HelloService")]

interface IHelloService

{

[OperationContract(IsOneWay = false)]

string Hello(string text);

}

Cannot Find Server (404) or Get Plain Text for WCF .svc Files From IIS?

Question

After upgrading WCF (or NetFx-3.0) from an older beta version to RC1, I get “Page Not Found” (404) error or get the plain text content of the .svc file for .svc files. Is .svc extension removed from IIS?

Why?

If you installed WCF or NetFx-3.0 RC1 or later versions, you may get this problem. On Windows XP SP2, you may get the plain text of the .svc file. On Windows 2003 Server, you may get error like “Cannot find the page” (404) error. It looks like .svc extension is not supported in IIS.

Is .svc mapping removed from IIS? No. Definitely not! The reason why you saw this is because your IIS Metabase got messed up by an earlier WCF installer due to a nasty bug. You need to clean up the IIS Metabase manually so that it works again. How? Please keep on reading.

Before RC1, there was a bug in WCF installer. It accidentally populates .svc mapping to every path/node in IIS Metabase. This messed up the Metabase and has potential security concern. Fortunately, we found this issue in RC1 and fixed it. By fixing it, we only install .svc mapping to the template of the root /W3SVC of IIS Metabase. However, those machines with earlier WCF beta versions installed still have the messy ScriptMaps. They stop the inheritance of the .svc ScriptMaps from the template at the root /W3SVC. This is especially true for the “Default Web Site” and its virtual applications.

If you create a new web site with WCF RC1 installed, you won’t have this problem for that web site.

Solutions

Here are different ways that can help you to resolve the problem:

· Download tool: CleanIISScriptMaps.exe

Please find the tool “CleanIISScriptMaps.exe” in the attached zip file. The source for the tool is also included. You also find this tool and corresponding information on http://wcf.netfx3.com/files/folders/product_team/entry5648.aspx. The usage for this tool is simple, just run it without any argument from any command window. After running it, everything should just work fine. You don’t need to rerun any other setup.

If you find that .svc is still not supported for some virtual directories but you want them to be supported as well, you can run the tool with more options to explicitly remove the ScriptMaps properties from those paths. Here is the full usage of the tool:

Usage: CleanIISScriptMaps

Options:

By default, the tool removes redundant ScriptMaps properties.

/f Remove ScriptMaps from all nodes in IIS Metabase except for

the root "/W3SVC".

/p Prompt for confirmation when removing ScriptMaps at a node

that is different than that of the root "/W3SVC".

/v Print out verbose information.

/? Print this help.

· Reinstall IIS or re-create web sites

You can uninstall IIS and reinstall it so that the IIS Metabase is cleaned up. Then you will need to run the WCF install tool manually as following:

"%windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe" /r /y

If you are running Windows 2003 Server, you may be able to delete the “Default Web Site” and re-create it. The problem will be fixed as well.

· Install .svc manually as a temporary workaround

You can run the following command to install .svc mapping manually. However, this does not clean up the mess of the IIS Metabase. You will see the same problem again when you install newer WCF. So this is the least recommended approach comparing to the above two options:

"%windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe" /s:W3SVC

Though this does not clean up the Metabase for you, it does not mess up new web sites.

How to use WSDualHttpBinding on Windows XP for Hosted Services

On Windows XP, Internet Information Service (IIS) does not use HTTP.syswhich is the HTTP driver that can be used to share HTTP traffic on the same port by multiple applications. This means that when IIS is running, the default port 80 is taken by IIS and no other applications can use it.

So if you use WSDualHttpBinding for your service, the client won’t be able to open the listener against the default port 80 on the same machine and it would fail with AddressAlreadyInUseException.

To workaround this, you need to change the ClientBaseAddress of WSDualHttpBinding on the client side so that it uses a different port than the one that IIS is using. From client config, you can do the following:

<system.serviceModel>

<client>

<endpoint name="WSDualOnXP"

address="http://localhost/WSDualOnXP/HelloWorld.svc"

binding="wsDualHttpBinding"

bindingConfiguration="WSDualOnXP"

contract="HelloWorldClient.IHelloContract" />

client>

<bindings>

<wsDualHttpBinding>

<binding name="WSDualOnXP"clientBaseAddress="http://localhost:808/WSDualOnXP/"/>

wsDualHttpBinding>

bindings>

system.serviceModel>

From code, you can also set the ClientBaseAddress:

WSDualHttpBinding binding = new WSDualHttpBinding();

binding.ClientBaseAddress ="http://localhost:808/WSDualOnXP/";

On the server side, you don’t have to specify the ClientBaseAddress since the addressing correlation is automatically done by WCF infrastructure.

WinFX February CTP is released!

GREAT NEWS! The WinFX February CTP has just been released today and can be downloaded from here. Here is the readme document. This CTP has consumed a lot of customer feedback and thus brings quite a lot of breaking changes over the January one. The detailed WCF breaking changes can be found here. The changes include namespaces, APIs, configuration, setup, etc. Ed Pinto has a quick blog entry on this.

The biggest change to WebHost is that the .svc file format is changed. This was actually reflected in my earlier blog entries.

Before:

<%@Service language="C#" Debug="true"class="Microsoft.ServiceModel.Samples.CalculatorService" %>

After:

<%@ServiceHost language="C#" Debug="true"Service="Microsoft.ServiceModel.Samples.CalculatorService" %>

You can also use the factory model to create your own ServiceHost with the following advanced syntax:

<%@ServiceHost language="C#" Debug="true"Factory="Microsoft.ServiceModel.Samples.CalculatorServiceHostFactory" Service="Microsoft.ServiceModel.Samples.CalculatorService" %>

The Visual Studio CTP for this WinFX can be found at this link.

The WinFX SDK is also released as part of the larger Windows SDK which includes pre-release documentation, samples, and tools for both Win32® and WinFX® in Windows Vista.