Service Development

Description Article
Table of Contents
Operations
Service Definition
How to do it

A WCF service is provided by defining one or more WCF endpoints.

An endpoint is defined by three elements:

  • [1] Address (defines the WHERE): The Uri to invoke that identifies the location of the service
  • [2] Binding (defines the HOW): Defines how the service can be accessed and communication is achived (e.g. what transport protocol, encoding and security to use):
Refer to types of binding article for full description.

 

  • [3] Service Contract (defines the WHAT): Defines what is exposed to the Client:
Type Purpose Defined by
Behavioral Contracts Defines what operations the Client can perform on a service, an defines what errors are raised by the service exposed ServiceContract, OperationContract, FaultContract
Structural Contracts Defines types what are moved between the service and Client endpoints DataContract

Note: The internal behavior of a service type can be adjusted using a family of classes and attributes (e.g. service must run in a multithreaded)
To develop a WCF service the following steps are required:

  1. Add to a separated file with the complex class types sent on the service request and expected as service response.These are the “Data Contract ” classes (refer to Data Representation section);
  2. Implement an interface with the required operations to be performed by the service (this is the “Service Contract” class);
  3. Add the ServiceContract attribute to the interface, and as good pratice, associated with a namespace;
  4. Add the OperationContract attribute to each method of (2);
  5. Add a class to implement the interface. This class is referenced as the “service type“.
  6. Associate an address and binding with the service type: This can be done via a configuration file or programtically within the host (in case of self-hosting scenario)

Note: The configuration file can be edited directly or via the “WCF configuration editor” on Visual Studio.

Note: Multiple endpoints with multiple binding types can be configured in order to serve different types of Clients


The Current Context

When programming service types, the “OperationContext” class can be used. It represents the runtime context of the service. Its behavior is similar to the “HttpContext” and “ContextUtil” classes.

Data Representation
How to do it

All the complex object data types required for parameterization of the invocation or expected as response of the requests must be marked with the DataContract attribute. This way the DataContractSerializer class will automatically translate .NET objects into XML for transmission to and from the server.

Such classes can be written manually, or generated from definitions of the types in XML Schema using the command-line XML Schemas/Data Types Support utility, “XSD.exe“.

Service Hosting

Every WCF service needs to have a host process : a windows service, IIS or console .NET program.

This host will create an instance of the System.ServiceModel.ServiceHost (or any custom System.ServiceModel.ServiceHostBase) and will manage the service configurations, behaviors, and channels.

Remarks

WCF makes use of the Windows Activation Services (WAS) component for service hosting:

Windows Process Activation Service (known as WAS) is the process activation mechanism introduced within Internet Information Services v7.0 and used by WCF services as hosting option.

It was built on the existing Internet Information Services v6.0 it is more powerful because it provides support for other protocols besides HTTP, such as TCP and Named Pipes, allowing the developers to choose the most appropriate protocol for their need.

How to do it

Refer to types of service hosting article for full description.

Service Hosting on IIS
How to do it

To host a service within IIS:

  • The service(s) type(s) are compiled into a class library assembly and then copied into the \bin subdirectory of that application root virtual directory;
  • A file called the service file (one file per WCF service) has the extension .”svc” and contains a @ ServiceHost directive that identifies the class that contains the code for the service:
<%@ ServiceHost Language="C#" Class="ServiceType" %>

and is copied into the virtual directory;

  • The configuration file is copied into the virtual directory and named “Web.config
  • The WCF service is then accessible by using the URL of the service file in the application root virtual directory.

If correctly hosted the broswer must responde to the URL with the Windows test page:

wcf  service hosted on IIS

How it works 

IIS gives on-demand loading and when a request comes in:

  • the Service Host (ServiceHost object) is constructed;
  • the service class hosted is instantiated and the request is handled.

and consequently:

  • setup requires more time and effort when a message comes in;
  • the programmer don’t have full control over the service host.

The virtual directory where the “.svc” file resides defines the address – any base addresses or explicitly defined addresses in the configuration are ignored:

  • the layout of the service addresses cannot be changed (e.g. http://servername/virtualdirectory/servicename.svc)

The “.svc” File

The “.svc” file is just a declaration of the service type and optionally the service host factory type.

This model is similar to the way ASMX pages are represented inside of an IIS application as “.asmx” files:

  1. A dedicated module exists within IIS that handles the “.svc” file type;
  2. In response to incoming messages, the directive  @ServiceHost  allows the module to automatically instantiate a host process and activated by the WCF hosting infrastructure.


How is it configured

The  service is configured in the Web.Config file, section “system.serviceModel” as exemplified next:

<?xml version="1.0" encoding="utf-8"?>
<system.serviceModel>
  <services>
    <service behaviorConfiguration="ServiceBehavior" name="WCF.wcfTestServiceImpl">
      <endpoint address="http://localhost:60063/TestService1.svc" binding="wsHttpBinding" contract="WCF.IwcfTestService"
                behaviorConfiguration="">
        <identity>
          <dns value="localhost"/>
        </identity>
      </endpoint>
      <endpoint address="mex" bindingConfiguration="" binding="mexHttpBinding" contract="IMetadataExchange" />
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="ServiceBehavior">
        <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
        <serviceMetadata httpGetEnabled="true"/>
        <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

Refer to the Service Configuration article for more details on the configuration settings.

Service Hosting in a managed application (Self-hosting)
How to do it

To host a service within a .NET application:

  • The service(s) type(s) are compiled into a class library assembly referenced by the application;
  • Program the application to host the service using the “ServiceHost” class
  • Provide an endpoint to the host via the application configuration file or progammatically.

The WCF service is then accessible by using the address provided for any endpoint configured. This address is relative to the host base address.

Note: The host can have one base address for each communication transport protocol.

If correctly hosted the application must be pre-launched in order to instantiate the ServiceHost class:

and the broswer must responde to the URL with the Windows test page:



How it works

The host service must be explicitly codified, constructed and initiated, nevertheless there is no on-demand loading whenever a message comes in since the Service Host (ServiceHost object) is already constructed and configured.

Due to it’s flexibility:

  • The programmer has full control over the lyfe cycle of the service host – when and how it’s constructed, etc;
  • The service addresses can be configured without the need to follow any pre-defined layout.

If self-hosting is considered then console application should be first used for testing, and next installed in a Windows Service on production environment.

Note: An hosting application can host multiple services by creating multiple instance of ServiceHost class

How is it configured

The  service is configured in the App.Config file, section “system.serviceModel“:

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="defaultServiceBehavior" name="WCF.wcfTestServiceImpl">
        <endpoint address="soap"  behaviorConfiguration="" binding="basicHttpBinding" contract="WCF.IwcfTestService">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="soap"  binding="netTcpBinding" contract="WCF.IwcfTestService"/>
        <endpoint address="mex" bindingConfiguration="" binding="mexHttpBinding" contract="IMetadataExchange" />
        <endpoint address="mextcp" bindingConfiguration="" binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:55545/training/wcf/app/testservice1"/>
            <add baseAddress="net.tcp://localhost:8000/training/wcf/app/testservice1"/>
          </baseAddresses>
        </host>
      </service>
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior name="defaultServiceBehavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

or programatically, as exemplified next (for simplicity only the web endpoints are considered):

ServiceHost host = null;
try
{
	host = new ServiceHost(
                            typeof(wcfTestServiceImpl),
                            new Uri("http://localhost:55545/training/wcf/app/testservice1"));

	BasicHttpBinding binding = new BasicHttpBinding (BasicHttpSecurityMode.None);

	ServiceEndpoint endPoint = new ServiceEndpoint(
					ContractDescription.GetContract(typeof(wcfTestServiceImpl)),
			                binding,
					"/soap");

	host.AddServiceEndpoint(endPoint);

	// Configure the MEX endpoint
	host.AddServiceEndpoint(
			ServiceMetadataBehavior.MexContractName,
			MetadataExchangeBindings.CreateMexHttpBinding(),
			"mex");

	// Configure service behavior: enable service metadata 

	ServiceMetadataBehavior serviceMetadataBehavior =
					new ServiceMetadataBehavior
					{
						HttpGetEnabled = true,
						MetadataExporter = {
							PolicyVersion = PolicyVersion.Policy15
						}
					};

	host.Description.Behaviors.Add(serviceMetadataBehavior);

	// Initiate host...

	host.Open();
	Console.WriteLine("Test console host initiated: Press <enter> to close host application");
	Console.ReadLine();
}
catch (Exception ex)
{
	Console.WriteLine("Test console host could not be initiated or an error occurred during host life cycle: " + ex.ToString());
}
finally
{
	if (host != null)
	{
		if (host.State != CommunicationState.Closed) host.Close();
	}
}

Refer to the Service Configuration article for more details on the configuration settings.

Service Hosting in a Windows Service
How to do it

To host a service within a .NET application:

  • The service(s) type(s) are compiled into a class library assembly referenced by the windows service;
  • Program the windows service to host the service using the “ServiceHost” class;
  •  Install the windows service using  ”installutil.exe utility application;
  • Provide an endpoint to the host via the application configuration file or progammatically.

The WCF service is then accessible by using the address provided for any endpoint configured. This address is relative to the host base address.

Note: The host can have one base address for each communication transport protocol.

How it works

The host service must be explicitly codified, constructed and initiated, nevertheless there is no on-demand loading whenever a message comes in since the Service Host (ServiceHost object) is already constructed and configured.

Due to it’s flexibility:

  • The service will be hosted, when system starts;
  • Process life time of the service can be controlled by Service Control Manager for windows service;
  • The service addresses can be configured without the need to follow any pre-defined layout.

Note: An hosting application can host multiple services by creating multiple instance of ServiceHost class

How is it configured

The  service is configured in the App.Config file, section “system.ServiceModel” as exemplified next:

(For programatically sample refer to the self-hosting example)

 <system.serviceModel>
    <services>
      <service behaviorConfiguration="defaultServiceBehavior" name="WCF.wcfTestServiceImpl">
        <endpoint address="soap"  behaviorConfiguration="" binding="basicHttpBinding" contract="WCF.IwcfTestService">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="soap"  binding="netTcpBinding" contract="WCF.IwcfTestService"/>
        <endpoint address="mex" bindingConfiguration="" binding="mexHttpBinding" contract="IMetadataExchange" />
        <endpoint address="mextcp" bindingConfiguration="" binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:55545/training/wcf/app/testservice1"/>
            <add baseAddress="net.tcp://localhost:8000/training/wcf/app/testservice1"/>
          </baseAddresses>
        </host>
      </service>
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior name="defaultServiceBehavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

Refer to the Service Configuration article for more details on the configuration settings.

Service Definition: Message Exchange Pattern (MEP)

MEPs describe the protocol of message exchanges a consumer must engage in to converse properly with the service.
WCF supports the following three MEPs:

  1. One-way

    In One-Way operation mode, the client will send a request to the server and will be blocked only for a moment till it dispatches its call to service.
    One-way operation can be enabled by setting IsOneWay property to” true” in the OperationContract attribute.

    Note: This MEP cannot be used in conjunction with the Fault Contract – Because it would be required a two-way channel.
  2. Request / Response [Default Message exchange pattern]

    The Client send a request to the WCF service and it will wait for the response from service (till configurated timeout).
    If the timeout configured in the binding expires the Client will receive a TimeOutException exception.
  3. Duplex

    Duplex MEP is a two-way message channel whose usage might be applicable in following situation:
    Consumer sends a message to the service to initiate longer-running processing and then subsequently requires notification back from the service, confirming the requested processing has been done.


    Duplex operation can be enabled by:

    • Create a call back contract and configure CallbackContract property in the ServiceContract attribute.
    • Configure the behavior for duplex communication and call the client function at the service
    • Configure the endpoint for duplex service


    Refer to here for code sample.


    Note:Duplex MEP is problematic and should be avoided.

WCF Error Handling

WCF service with no error-handling code (i.e. no try-catch block) will catches the exception: It sends to the client a FaultException with the following message:

“The server was unable to process the request due to an internal error.”

This is the usual behavior whenever there’s an unhandled exception. WCF doesn’t send to the client the exception message or the stack trace or any of the other information that was contained in the exception.

Remarks

The reason for WCF avoid internal error infomation are:

  • The Exception class is specific to .NET. WCF was designed to make web services that can be called by clients not written in .NET;
  • Security reasons: It’s not safe to provide the stack trace to anyone that may call. Detailed error messages are also risky.

For example, it’s not safe to inform the caller that the database insert failed because specific user name is already in use.

The safer practice is to write this information to an error log and provide much less detailed error information to the caller.

How to do it

The FaultException class

The FaultException class includes these constructors:

public FaultException(string reason);
public FaultException(string reason, FaultCode code);
public FaultException(FaultReason reason);
public FaultException(FaultReason reason, FaultCode code);
//others

Through the several FaultException constructors, its possible to create a FaultException with a string, a FaultCode, a FaultReason or some combination of these.

The service can provide information as show next:

try
{
    // do something
}
catch (Exception ex)
{
     Log.LogException(ex);
      FaultCode code = new FaultCode("Invalid Operation");
     throw new FaultException("Customer name cannot be null.", code);
}

The client can capture the FaultException and read the error data as follows:

try
{
     // call the web service
}
catch (FaultException ex)
{
     Console.WriteLine("FaultCode: " + ex.Code);
     Console.WriteLine("Message: " + ex.Message);
}

The FaultException<T> class

With the FaultException<T> class its possible to pass in some combination of string, FaultCode and FaultReason as wll as some additional value T. T can be a value or an entire class object instantiated from a custom error class as show next:

[DataContract]
public class ErrorInformation
{
    [DataMember]
    public bool Result { get; set; }
    [DataMember]
    public string ErrorLevel1 { get; set; }
    [DataMember]
    public string ErrorLevel2 { get; set; }
}

The custom error message classe will be used in the service as follows:

try
{
      // Do something
}
catch (Exception ex)
{
  ErrorInformation _error = new ErrorInformation()
  _error.Result = false;
  _error.ErrorLevel1 = "Internal server error.";
  _error.ErrorLevel2 = ex.ToString();
  throw new FaultException<ErrorInformation>(_error, ex.ToString());
}

The client can catch this FaultException as follows:

try
{
      // call the web service
}
catch (FaultException<ErrorInformation> ex)
{
  Console.WriteLine("Error Message 1" + ex.Detail.ErrorLevel1 + Environment.NewLine);
  Console.WriteLine("Error Message 2" + Environment.NewLine + ex.Detail.ErrorLevel2);
  Console.ReadLine();
}

When using the FaultException<T> class, the FaultContract tells the client program what type of Faults each method can throw, and must be included on the service operation(s) declaration:

[ServiceContract]
interface ITestService
{
  [OperationContract]
  [FaultContract(typeof(ErrorInformation))]
  int DoWork();
}

This FaultContract informs the client that when it calls “DoWork()” it may receive a fault of type FaultException<ErrorInformation>.