Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Tuesday, December 22, 2009

Convert Exceptions in WCF Services to Fault Exceptions a Client Can Use

This article will show you how to trap an exception that occurs within a WCF service but also inform the client application of the exception so it can be handled.  To do this we will make use of a custom faultException.

1. You need to create a fault contract for the type of fault that you want to be returned to the client via WCF.  Below I have created a ‘DBConcurrencyFault’ fault that tells the client when a database concurrency violation has occurred (e.g. 2 people tried to save the same record).

namespace Common.Services.Faults
{
    [DataContract]
    public class DBConcurrencyFault
    {
        [DataMember]
        public String Message { get; set; }

        [DataMember]
        public String ExceptionType { get; set; }
    }
}

2. Now we are going to create a new attribute that we can use to decorate our service classes with.  This attribute will trap any exception occurring in a WCF service and convert them into a specific fault exception.

namespace Common.Services
{
    public class WcfErrorHandler : Attribute, IErrorHandler, IServiceBehavior
    {
        public bool HandleError(Exception error)
        {
            return false;
        }
       
        public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
        {
            if (fault != null)
                return;

            if (error.GetType() == typeof(System.Data.DBConcurrencyException)) 
            {
                // Handle Database record being written to by different users (Conncurrency Fault)
                DBConcurrencyFault databaseConcurrencyFault = new DBConcurrencyFault();
                databaseConcurrencyFault.Message = error.Message;
                databaseConcurrencyFault.ExceptionType = error.GetType().ToString();
                String faultReason = "Someone else has already saved this record.";
                FaultException<DBConcurrencyFault> faultException = new FaultException<DBConcurrencyFault>(databaseConcurrencyFault, faultReason);
                MessageFault messageFault = faultException.CreateMessageFault();
                fault = Message.CreateMessage(version, messageFault, faultException.Action);
            }
            else
            {
                // Handle all other errors as standard FaultExceptions
                FaultException faultException = new FaultException(error.Message);
                MessageFault messageFault = faultException.CreateMessageFault();
                fault = Message.CreateMessage(version, messageFault, faultException.Action);
            }
        }

        #region IServiceBehavior Members
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {           
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher disp in serviceHostBase.ChannelDispatchers)
            {
                disp.ErrorHandlers.Add(this);
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {           
        }
        #endregion
    }
}

Note: the ‘ProvideFault’ method contains code that determines if an exception of type ‘System.Data.DBConcurrencyException’ has occurred.  If one has it is converted into a ‘DBConcurrencyFault’ which is then returned to the calling client by the WCF service. 

3. Now you can either add the WcfErrorHandler attribute to all your wcf services, or you can as I have below define a base service class that all your service classes will inherit from.

namespace Common.Services
{
   [WcfErrorHandler()]
   public class BaseService
   {
       // Common service methods go here
   }
}

4. Have your service class inherit from the base service class. 

public class MileageClaimService : BaseService, IMileageClaimService
{
      // Service methods go here
}

5. Trap the fault exception from a web client and redirect the user to a tailored error page.
   5a.  For an Asp.Net web application you can add the exception handling code to the global.asax Application_Error method.

protected void Application_Error(object sender, EventArgs e) 

     Exception ex = Server.GetLastError().GetBaseException(); 

     if (ex.GetType() == typeof(System.ServiceModel.FaultException<Common.Services.Faults.DBConcurrencyFault>) ) 
     { 
        Server.ClearError(); 
        Response.Redirect("~/Errors/ErrorDbConcurrency.aspx"); 
     } 
}

  5b. From an Asp.Net MVC web application you just add a HandleError attribute to the controller with the type of DBConcurrencyFault and redirect to a specific view.

[HandleError(ExceptionType = typeof(System.ServiceModel.FaultException<Common.Services.Faults.DBConcurrencyFault>),
View = "ErrorDbConcurrency")]
[HandleError]
public class ClaimController : Controller
{
    // Controller methods go here
}

Note: There are 2 HandleError attributes, the first handles the DBConcurrencyFault and redirects to a custom error page.  The 2nd traps all other exceptions and redirects to the default error page.  The order of these 2 HandleError attributes is important.

Friday, December 18, 2009

Constructor Dependency Injection with a WCF Service using Unity

In a previous article I showed how to use dependency injection to specify in a config file which repository should be used by a non WCF service.

This article will show how to do the same but for a WCF service.

The previous article can be found here.

1. WCF uses interfaces so the first thing we need to do is add an interface to our service to WCF enable it.  Since the service is called MileageClaimService I will call the interface IMileageClaimService. Below is the interface definition decorated with WCF ServiceContract and OperationContract attributes.

namespace Service.Finance.Mileage.Interfaces
{
    [ServiceContract(
        Name = "MileageClaimService",
        Namespace = "http://mycompany.com/services/finance/v1.0.0.0")]
    public interface IMileageClaimService
    {
        [OperationContract(Name = "GetAllClaimsForEmployee")]
        List<DTO.Finance.Mileage.MileageClaim> GetAllClaimsForEmployee(string employeePayrollNumber);

        [OperationContract(Name = "GetClaimById")]
        DTO.Finance.Mileage.MileageClaim GetClaimById(int id);

        [OperationContract(Name = "GetClaimByReferenceNumber")]
        DTO.Finance.Mileage.MileageClaim GetClaimByReferenceNumber(string referenceNumber);

        [OperationContract(Name = "Save")]
        [FaultContract(typeof(DBConcurrencyFault))]
        void Save(ref DTO.Finance.Mileage.MileageClaim claim);

        [OperationContract(Name = "Delete")]
        void Delete(DTO.Finance.Mileage.MileageClaim claim);

        [OperationContract(Name = "SubmitClaim")]
        void SubmitClaim(DTO.Finance.Mileage.MileageClaim claim);
    }
}

Below I’ve added the interface and the ServiceBehavior attribute to the service.

namespace Service.Finance.Mileage
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class MileageClaimService : IMileageClaimService
    {
        IMileageClaimRepository _mileageClaimRepository;
       [InjectionConstructor] 
        public MileageClaimService(IMileageClaimRepository  mileageClaimRepository)
        { 
        }

        // Other service methods go here 
    }
}

2.  We need to create the Unity classes for WCF.
I’ve created a project called Common. In here I’m going to place the Unity WCF classes (there are 3 of them) in a directory called Services\Unity.  You can just copy the code to your own project as I did (thanks to dperez for helping me with this: http://blogs.southworks.net/dperez/2008/11/02/unity-friends-the-wcf-service-host-side/).

InjectionBehavior.cs

using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.Unity;

// WCF Unity Injection Behavior
namespace Common.Services.Unity
{
    /// <summary>
    /// Creates services instance by using Unity type resolution.
    /// </summary>
    public class InjectionBehavior : IServiceBehavior
    {
        /// <summary>
        /// Injection container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Initializes a new instance of the InjectionBehavior class.
        /// </summary>
        /// <param name="container">Injection container.</param>
        public InjectionBehavior(IUnityContainer container)
        {
            this.container = container;
        }

        /// <summary>
        /// Provides the ability to pass custom data to binding elements to support the contract implementation.
        /// </summary>
        /// <param name="serviceDescription">The service description of the service.</param>
        /// <param name="serviceHostBase">The host of the service.</param>
        /// <param name="endpoints">The service endpoints.</param>
        /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
        public void AddBindingParameters(
            ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase,
            Collection<ServiceEndpoint> endpoints,
            BindingParameterCollection bindingParameters)
        {
        }

        /// <summary>
        /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase"> The host that is currently being built.</param>
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            foreach (var channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                var channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                if (channelDispatcher != null)
                {
                    foreach (var endpointDispatcher in channelDispatcher.Endpoints)
                    {
                        endpointDispatcher.DispatchRuntime.InstanceProvider = new InjectionInstanceProvider(this.container, serviceDescription.ServiceType);
                    }
                }
            }
        }

        /// <summary>
        /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
        public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
        }
    }
}

InjectionElement.cs
using System;
using System.Configuration;
using System.ServiceModel.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

// WCF Unity Injection Element
namespace Common.Services.Unity
{
    /// <summary>
    /// Injection element class representing a behavior extension configuration tag.
    /// </summary>
    public class InjectionElement : BehaviorExtensionElement
    {
        /// <summary>
        /// Default Unity configuration section path.
        /// </summary>
        private const string DefaultUnityConfigurationSectionPath = "unity";

        /// <summary>
        /// Initializes a new instance of the InjectionElement class.
        /// </summary>
        public InjectionElement()
        {
            this.UnityConfigurationSectionPath = DefaultUnityConfigurationSectionPath;
        }

        /// <summary>
        /// Gets the type of behavior.
        /// </summary>
        /// <value>Behavior type.</value>
        public override Type BehaviorType
        {
            get
            {
                return typeof(InjectionBehavior);
            }
        }

        /// <summary>
        /// Gets or sets the injection container name.
        /// </summary>
        /// <value>Container name.</value>
        [ConfigurationProperty("containerName")]
        public string ContainerName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the Unity configuration section path to read configuration from.
        /// </summary>
        /// <value>Configuration path.</value>
        [ConfigurationProperty("unityConfigurationSectionPath", DefaultValue = DefaultUnityConfigurationSectionPath)]
        public string UnityConfigurationSectionPath
        {
            get;
            set;
        }

        /// <summary>
        /// Creates a behavior extension based on the current configuration settings.
        /// </summary>
        /// <returns>The behavior extension.</returns>
        protected override object CreateBehavior()
        {
            var unitySection = ConfigurationManager.GetSection(this.UnityConfigurationSectionPath) as UnityConfigurationSection;
            IUnityContainer container = new UnityContainer();
            if (this.ContainerName == null)
            {
                unitySection.Containers.Default.Configure(container);
            }
            else
            {
                unitySection.Containers[this.ContainerName].Configure(container);
            }
            return new InjectionBehavior(container);
        }
    }
}

InjectionInstanceProvider.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.Unity;

// WCF Unity Injection Provider
namespace Common.Services.Unity
{
    /// <summary>
    /// Injection instance provider for new service instances.
    /// </summary>
    internal sealed class InjectionInstanceProvider : IInstanceProvider
    {
        /// <summary>
        /// Injection container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Type of the service implementation.
        /// </summary>
        private Type instanceType;

        /// <summary>
        /// Initializes a new instance of the InjectionInstanceProvider class.
        /// </summary>
        /// <param name="container">Injection container.</param>
        /// <param name="instanceType">Type of the service implementation.</param>
        public InjectionInstanceProvider(IUnityContainer container, Type instanceType)
        {
            this.container = container;
            this.instanceType = instanceType;
        }

        /// <summary>
        /// Returns a service object given the specified System.ServiceModel.InstanceContext object.
        /// </summary>
        /// <param name="instanceContext">The current System.ServiceModel.InstanceContext object.</param>
        /// <param name="message">The message that triggered the creation of a service object.</param>
        /// <returns>The service object.</returns>
        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return this.container.Resolve(this.instanceType);
        }

        /// <summary>
        /// Returns a service object given the specified System.ServiceModel.InstanceContext object.
        /// </summary>
        /// <param name="instanceContext">The current System.ServiceModel.InstanceContext object.</param>
        /// <returns>The service object.</returns>
        public object GetInstance(InstanceContext instanceContext)
        {
            return this.GetInstance(instanceContext, null);
        }

        /// <summary>
        /// Called when an System.ServiceModel.InstanceContext object recycles a service object.
        /// </summary>
        /// <param name="instanceContext">The service's instance context.</param>
        /// <param name="instance">The service object to be recycled.</param>
        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            this.container.Teardown(instance);
        }
    }
}

3. Now we need to update the app.config file for the service project.  Below shows the configuration to enable the service for Unity dependency injection (it’s exactly the same as in the previous post).

<configSections>
  <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>

<unity>
    <containers>
      <container>
        <types>
           <type type="DataAccess.Finance.Mileage.Interfaces.IMileageClaimRepository,
                            DataAccess.Finance.Mileage.Interfaces"
                    mapTo="EF.DataAccess.Finance.Mileage.Repositories.MileageClaimRepository,
                            EF.DataAccess.Finance.Mileage"/>
        </types>
      </container>
    </containers> 
</unity>

And below is the new configuration to enable the service for WCF using the Unity behavior.

<system.serviceModel> 
    <extensions>
      <behaviorExtensions>
        <add name="unity" type="Common.Services.Unity.InjectionElement, Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <bindings>
      <netTcpContextBinding>
        <binding name="netTcpContextBindingConfig" transactionFlow="true" />
      </netTcpContextBinding>
    </bindings>

    <services>
      <service behaviorConfiguration="Service.ServiceBehavior" name="Service.Finance.Mileage.MileageClaimService">
        <endpoint address="" binding="netTcpContextBinding" bindingConfiguration="netTcpContextBindingConfig"
          name="TcpBinding_MileageClaimService" contract="Service.Finance.Mileage.Interfaces.IMileageClaimService" />
        <endpoint address="mex" binding="mexTcpBinding" name="MexTcpBinding_MileageClaimService"
          contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:8734/Services.Finance/MileageClaimService/v1.0.0.0" />
          </baseAddresses>
        </host>
      </service>
    </services>

     <behaviors>
      <serviceBehaviors>
        <behavior name="Service.ServiceBehavior">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="false" />
          <unity />
        </behavior>
      </serviceBehaviors>
    </behaviors> 
</system.serviceModel>

Note the behaviorExtensions section contains a Unity entry.  This points to the Unity InjectionElement class you added in the last section. 

Also note the <unity /> line in the serviceBehaviors. 

Both of these are critical to enabling the WCF service to use Unity dependency injection.

Thats it, you can use the wcf service as you would do any wcf service by adding a service reference to your client project and calling the methods on it.  The repository will be injected automatically into the constructor on the service because of the behavior extensions defined in the wcf configuration.