Defining Exception and Error Classes
The DCS Software Factory can generate exception and error classes that let you implement strongly typed error handling in your services and client applications.
You use the Add Error and Exception Classes recipe in the Messages project to define Exception and Error classes. For more information about creating a Messages project, see Defining Exception and Error Classes.
Note
You can use the default exception and error message classes generated automatically by the DCS Software Factory if you do not create a Messages project and your operations do not need to customize the exceptions and errors that they can throw.
To add exception and error classes to a messages project
Right-click the message project, and then click Add Error and Exception Classes.
In the Add Error and Exception Classes dialog box, type names for the exception and error classes, and then click Finish.
-
Note
When you create error and exception messages, it is common practice to add an Error or Exception suffix to the class names..
In Solution Explorer, double-click the source code file for the error and exception classes to display them in the Code and Text editor.
Add properties and methods that define the data and functionality of the error and exception classes.
-
Note
To enable the serializer to understand that the property requires the service to serialize a message before sending it. You should decorate any custom properties that should be serialized with the DataMember attribute.
When you complete the customization of the error and exceptions classes, save the files.
Note
If you only need to define an exception message class, use the Add Exception Class recipe of the message project.
The default exception and error classes generated by the DCS Software Factory provide a basic level of fault handling. For example, if you use the DCS Software Factory to create a new operation called PlaceOrder in a service called OrderService, and you define an error class called PlaceOrderError, the DCS Software Factory generates a fault contract for the PlaceOrder operation that resembles the following code example.
[OperationContract(...)]
[FaultContract(typeof(OrderService.PlaceOrderError))]
[FaultContract(typeof(FrameworkError))]
...
OrderService.ResponseMessage PlaceOrder(OrderService.RequestMessage request);
A fault contract enables a service operation to indicate the types of exception that it can throw. When the service throws an exception it wraps the exception in a serializable fault object that is passed back to the client application. In this example, there are two serializable fault types that the PlaceOrder operation can throw; PlaceOrderError and FrameworkError. For more information about fault contracts, see Specifying and Handling Faults in Services and Contracts.
The FrameworkError Class
The FrameworkError class is the base class for all serializable DCS fault objects. It provides properties that an operation can use to specify the reason for an exception and the location of that exception. The following code example shows how DCS implements this class.
using System;
using System.Runtime.Serialization;
namespace Microsoft.ConnectedIndustry.ServiceModel.Common
{
[Serializable]
[DataContract(Name = "FrameworkError", Namespace= "https://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/ProcessExecution")]
public class FrameworkError
{
public FrameworkError();
public FrameworkError(Exception exception);
public FrameworkError(FrameworkException exception);
public FrameworkError(string exception);
public FrameworkError(string exception, string innerException);
[DataMember]
public string AppDomain { get; set; }
[DataMember]
public string AssemblyName { get; set; }
[DataMember]
public string ClassName { get; set; }
[DataMember]
public string Exception { get; set; }
[DataMember]
public string InnerException { get; set; }
[DataMember]
public string Machine { get; set; }
[DataMember]
public string MethodName { get; set; }
[DataMember]
public string ProcessId { get; set; }
[DataMember]
public string ProcessName { get; set; }
[DataMember]
public ReasonEnum Reason { get; set; }
[DataMember]
public int ThreadId { get; set; }
}
}
When an operation detects an exception condition, it can create an instance of this class and throw a WCF FaultException exception based on this class. The following code shows an example:
...
if (orderValue < 0) // Error. Orders cannot have a negative value
{
FrameworkError err = new FrameworError("Orders cannot have a negative value");
throw new FaultException<FrameworkError>(err);
}
...
Implementing a Custom Error Class
The custom error class that the DCS Software Factory generates lets you extend the FrameworkError class and add business-specific properties and functionality. The following code example shows the custom error class that the DCS Software Factory generates, with some customizations shown in bold
Note
Note: In the example, PlaceOrderError is the name of the error class specified by the user when the user ran the Add Error and Exception Classes recipe. For more information, see Walkthrough: Creating a DCS Messages Project.
You should add any properties that enable the operation to specify the complete reason for the error. An extension to the previous example includes the quantity of the item that the client application attempts to order in the error class. If the operation cannot fulfill the order because of insufficient stock, the GoodsExpectedDate property specifies when additional stock is expected. A client application can examine this information and try again on that date.
#region References
using System;
using System.ServiceModel;
using System.Runtime.Serialization;
using Microsoft.ConnectedIndustry.ServiceModel.Common;
#endregion
namespace OrderService
{
/// <summary>
/// This class contains information about an exception
/// that occurred during the execution of a task.
/// </summary>
[Serializable]
[DataContract(Namespace = "http://OrderService")]
public class PlaceOrderError : FrameworkError
{
#region Private Constants
private const string MessageUnknown = "An error occurred during the execution of a task";
#endregion
#region Public Constructors
/// <summary>
/// Initiates a new instance of the PlaceOrderError class.
/// </summary>
public PlaceOrderError() : base()
{
// TODO: Type logic here
}
/// <summary>
/// Initiates a new instance of the PlaceOrderError class.
/// </summary>
/// <param name="exception">The occurrence of FrameworkException causing the failure.</param>
public PlaceOrderError(FrameworkException exception) : base(exception)
{
// TODO: Type logic here
}
// Customizations private int qtyOfItemsInOrder; private DateTime goodsExpectedDate; [DataMember] public int QtyOfGoodsInOrder { get { return qtyOfItemsInOrder; } set { qtyOfItemsInOrder = value; } } [DataMember] public DateTime GoodsExpectedDate { get { return goodsExpectedDate; } set { goodsExpectedDate = value; } } public PlaceOrderError(DateTime goodsExpected, int itemsInOrder) : base() { GoodsExpectedDate = goodsExpected; QtyOfGoodsInOrder = itemsInOrder; }
#endregion
}
}
If the PlaceOrder operation detects that there is insufficient stock available to fulfill an order, it can create an instance of the PlaceOrderError class, populate it with the appropriate information, and throw a WCF FaultException exception. The following code is an example.
...
// The variable request is the request message sent to the operation
// The SufficientGoodsInStock method determines whether the warehouse currently has
// sufficient goods in stock to satisfy the order in the request
if (!SufficientGoodsInStock(request))
{
// The NumberOfItems property in the request object
// specifies the volume requested by the client
int qtyOfGoodsInOrder = request.NumberOfItems;
// Find when goods will be available DateTime goodsWillBeAvailableWhen = ...;
// Create a new instance of the PlaceOrderError class and throw a fault
PlaceOrderError pErr = new PlaceOrderError(qtyOfGoodsInOrder, goodsWillBeAvailableWhen);
throw new FaultException<PlaceOrderError>(pErr);
}
...
The FrameworkException Class
DCS provides the FrameworkException class as the base class for exceptions that an operation can detect and handle. The exception handling code in an operation can catch a FrameworkException exception and then use this object to build a FrameworkError object that it can throw as a FaultException. The following example shows how to catch a FrameworkException exception and throw a PlaceOrderError fault (the PlaceOrderError class extends the FrameworkError class).
...
catch (FrameworkException fEx)
{
PlaceOrderError err = new PlaceOrderError(fEx);
throw new FaultException<PlaceOrderError>(err);
}
...
The FrameworkException class extends the .NET Framework ApplicationException class. It is a serializable class that contains a number of properties that contain data about the cause of the exception. The following table describes each of the properties defined in the FrameworkException class.
Property Name |
Description |
---|---|
AppDomain |
A string representation of the application domain in which the exception occurred. |
AssemblyName |
A string representation of the assembly name that contains the method in which the exception occurred. |
ClassName |
The name of the class in which the exception occurred. |
Handled |
A Boolean value that indicates whether the exception has been handled. Exception handling code should check this value to see whether any further handling is necessary. If the error handling code resolves the exception, set this to True. |
Machine |
The name of the machine on which the exception occurred. If you use multiple servers for service hosting, this property shows which machine experienced the exception. |
MethodName |
The name of the method that threw the exception |
ProcessId |
The process ID under which the service was running when the exception occurred. |
ProcessName |
The process name, related to the process ID, under which the service was running when the exception occurred. |
Reason |
An enumeration defining the reason for the error. This property can have one of the following six values: |
ThreadId |
The thread ID of the thread that threw the exception. |
When you add an exception class to a messages project by using the Add Error and Exception Class or Add Exception Class recipes, the DCS Software Factory generates an exception class that extends the FrameworkException class. The following example shows a class called OrderException created in this way.
Note
The FrameworkException class was designed and optimized for use by DCS services. You should use the FrameworkExpection class as a base class for your own exception classes, rather than the .NET Framework ApplicationException class.
#region References
using System;
using System.Runtime.Serialization;
using Microsoft.ConnectedIndustry.ServiceModel.Common;
#endregion
namespace OrderService
{
/// <summary>
/// This class contains information about an exception
/// that occurred in a DCS PlaceOrder task.
/// </summary>
[Serializable]
public class PlaceOrderException : FrameworkException
{
#region Public Constructors
/// <summary>
/// Initializes a new instance of the PlaceOrderException class.
/// </summary>
public PlaceOrderException() : base()
{
}
/// <summary>
/// Initializes a new instance of the PlaceOrderException class with a specified error message.
/// </summary>
/// <param name="message">A message that describes the error.</param>
public PlaceOrderException(string message) : base(message)
{
}
/// <summary>
/// Initializes a new instance of the PlaceOrderException class
/// with a specified error message and a reference to the inner exception
/// that is the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception.
/// If the innerException parameter is not a null reference, the current exception is raised
/// in a catch block that handles the inner exception.</param>
public PlaceOrderException(string message, Exception innerException) : base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the PlaceOrderException class with serialized data.
/// </summary>
/// <param name="info">The object that holds the serialized object data.</param>
/// <param name="context">The contextual information about the source or destination.</param>
public PlaceOrderException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endregion
}
}
You can customize this class and include additional properties specific to your service. Your operation can then detect exception conditions, and throw and catch exceptions of this type. An exception handler can construct a FrameworkError object by using this type and transmit it as a FaultException exception to a client application. The following code shows an example.
...
try
{
...
... // detect an error condition and throw a PlaceOrderException
if (...)
{
throw new PlaceOrderException(...);
}
...
}
catch (PlaceOrderException pEx)
{
PlaceOrderError err = new PlaceOrderError(pEx);
throw new FaultException<PlaceOrderError>(err);
}
...
See Also
Defining Request and Response Messages