Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Service Warning in WCF using Determined Interaction Pattern - Part 1

0.00/5 (No votes)
30 Sep 2010CC (ASA 2.5)4 min read 15.4K  
This article explains how to generate, convey and handle warnings in SOA system.

The complete source code for this article can be downloaded at http://www.udooz.net/file-drive/doc_download/16-determinedinteraction.html.

Read my determined interaction pattern for following this article.

Introduction

Handling and propagating errors in a SOA system is well known and few patterns are already available for this. An interaction (means that when a consumer invokes a service) may fail due to the incorrectness request data or problem in the service layer itself. This makes an interaction as faulted. There are some business scenarios where an interaction might not be required to fault, when the cause of failure is accepted by the consumer. This means that overriding that failure. This kind of failure is called as warning. Determined Interaction is a pattern to handle and propagate warnings in SOA systems. This article explains the implementation of exposing warning in WCF services based on my WCF pattern Determined Interaction. Read the pattern here. I've taken the same drug prescription for this article.

The following figure shows the responsibilities of each layer of a SOA system to handle warning.

Layer Responsibilities

Core Objects at Service Layer

The following class diagram shows the core objects required to convey warnings.

Core Object

In a service with request and response message exchange pattern, a medium is required to convey if any warning occurred during an interaction. Acknowledgement is acted as a medium which conveys the following:

  • Status - status of the interaction which could be either success or failure. I've defined this as integer and mimic HTTP status code 200 (Success) and 301 (Warning)
  • TimeStamp - when the result happened
  • Warnings - contains warnings if any

Warning contains Code which uniquely represents a warning type, for example I've used 12345 for drug allergy with relevant Message which contains description of the warning. CorrelationState contains the hashed value of data which is required for interaction integrity.

Two interactions might be possible, one ends up with conveying warning details to the consumer followed by approval interaction with the same set of information in addition with correlation state, if consumer wants to override the warnings. Both interactions have to be handled by single code path in the domain layer. AddWarning() method of Acknowledgement class processes both interactions and gives the result as WarningElevation. WarningElevation.Admonish means that the interaction should be halted and convey the warning details to the consumer. Override means that this is second interaction (i.e. determined interaction) and consumer wants to override the warning. Let us see the implementation of AddWarning() step by step.

Exploring AddWarning()

The signature of this method is:

C#
public static WarningElevation AddWarning
  (int code, string message, DomainBase[] admonishedObjects, object[] otherIntegrities)

The first two parameters are self-explanatory. The domain objects could be the place in the domain layer where correlation state on every item level can be conveyed. Hence, I've declared a base class (though I'm in favour of POCO) DomainBase with correlation state array in the domain layer as shown in the below code:

C#
[Serializable]
public class DomainBase
{
	[NonSerialized]
	public string[] CorrelationStates;
}

You may be surprised that CorrelationStates is an array, because there would be more than one warning possible during an interaction. Let us see the first part of AddWarning() method.

C#
string generatedCorrelationState = null;
int eludeCount = 0;
int admonishedCount = 0;

if (admonishedObjects != null && admonishedObjects.Length > 0)
{
	string[] cstates = admonishedObjects[0].CorrelationStates;
	bool verifyMode = (cstates != null && cstates.Length > 0 && 
		!string.IsNullOrEmpty(cstates[0])) ? true : false;
	
	generatedCorrelationState = new ObjectBytifier(verifyMode, code, 
		admonishedObjects, otherIntegrities).Stringified;
	admonishedCount = admonishedObjects.Length;
	eludeCount = admonishedObjects.Count(admonishedObj =>
	{
		return admonishedObj != null &&
			admonishedObj.CorrelationStates != null &&
			admonishedObj.CorrelationStates.Length > 0 &&
			admonishedObj.CorrelationStates.Contains
				(generatedCorrelationState);
	});
}

This part will be executed when at least an object is given in the admonishedObjects. If any of the objects in admonishedObject contains CorrelationState, this interaction will be decided as determined interaction. Every time, a correlation state is being generated and kept in generatedCorrelationState variable by using ObjectBytifier. This will be conveyed to the consumer for the first interaction. During the determined interaction, this is used to verify the one coming from the consumer. eludeCount stores how many admonishedObjects contain the correlation state during the determined interaction. Let us see the remaining part of AddWarning().

C#
if (eludeCount == 0 && admonishedCount != eludeCount)
{
	string language = string.Empty;
	Acknowledgement currentAck = Acknowledgement.Current;         
					
	if (currentAck.Warnings == null)
		Acknowledgement.Current.Warnings = new List<Warning>();
	
	Warning fault = new Warning
	{
		Code = code,
		Message = message,
		CorrelationState = generatedCorrelationState
	};

	Acknowledgement.Current.Warnings.Add(fault);
	Acknowledgement.Current.Status = 301;
	return WarningElevation.Admonish;
}else return WarningElevation.Admonish;

When no correlation state is available in the admonishedObjects or no matched correlation state, warning has been generated and added to the Acknowledgement. Based on this, appropriate WarningElevation has been returned. Let us see the another important object which generates correlation state.

Exploring ObjectBytifier

ObjectBytifier class

The ObjectBytifier contains properties to set all interaction integrity elements AdmonishedObjects, OtherIntegrities, UserId and WarningCode. The OtherIntegrities is a place holder wherein if you want to place anything other than domain objects, for example, in my sample, I've used this to place patient ID which is nothing but a string, but required for integration integrity.

Exploring BytifyBaseObject()

Bytifier property converts all interaction integrity elements into byte array and Stringified element converts that byte array into fixed size string. This is what we called as CorrelationState. BytifyBaseObjects() is the vital method here. Let us see the initial part of it.

C#
string salt = string.Format("{0}{1}{2}", "SALT",
	UserId, WarningCode);
_bytified = ASCIIEncoding.UTF8.GetBytes(salt);

if (AdmonishedObjects != null && AdmonishedObjects.Length > 0)
{
	BinaryFormatter serializer = new BinaryFormatter();
	MemoryStream memStream = null;

	for(int i = 0; i < AdmonishedObjects.Length; i++)
	{
		if (AdmonishedObjects[i] != null)
		{
			using (memStream = new MemoryStream())
			{
				serializer.Serialize(memStream, AdmonishedObjects[i]);
				_bytified = _bytified.Concat
					(memStream.ToArray()).ToArray();
				memStream.Close();
			}
		}
	}                
}

The initial string has been formed with salt, user id and warning code followed by all the AdmonishedObjects are serialized using BinaryFormatter and stored into _bytified field.

C#
if (OtherIntegrities != null && OtherIntegrities.Length > 0)
{
	BinaryFormatter serializer = new BinaryFormatter();
	MemoryStream memStream = null;


	for (int i = 0; i < OtherIntegrities.Length; i++)
	{
		if (OtherIntegrities[i] != null)
		{
			using (memStream = new MemoryStream())
			{
				serializer.Serialize(memStream, OtherIntegrities[i]);
				_bytified = _bytified.Concat
					(memStream.ToArray()).ToArray();
				memStream.Close();
			}
		}
	} 
}
_bytified = new MD5CryptoServiceProvider().ComputeHash(_bytified)

Followed by admonishedObjects, OtherIntegrities has been bytified. Finally a fixed hash has been generated by using System.Security.Cryptography.MD5CryptoServiceProvider and stored again into _bytified field.

Let us see the remaining in part 2 of this article.

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution-ShareAlike 2.5 License