Sunday, February 13, 2011

A Messaging System across Layers

Often, while developing custom applications to client we come across situations where-in a decision is taken to store all application messages within the database rather than picking them out of resource files. Also, in many cases we end up going in for a basic 3-tier architecture minus the Service Layer.

When such decisions are taken, we end up passing a "ref" variable from the UI layer onto the Business Layer to set the message and return it back to the UI layer. In order to alleviate this problem, I came up with a simple message system. This blog post will discuss the basic idea behind this simple messaging system. In the next blog post, I will show how we can take advantage of the new caching mechanism provided by .NET framework 4.0 via the System.Runtime.Caching namespace. Using this, we can cache all the messages at application startup.

The basic idea is to have an interface, say "IResultBase" that will act as a container for the message and will also indicate the message type. The code for this will look something like this:

public enum MessageTypeEnum
{
Success,
Failure,
None
}
public interface IResultBase
{
MessageTypeEnum MessageType { get; set; }
string Message { get; set; }
}



Then we create a couple of interfaces, namely "IResult" and "IResultList". The code for them would be:

public interface IResult<T> : IResultBase
{
T Result { get; set; }
}

public interface IListResult<T> : IResultBase
{
IList<T> Result { get; set; }
}


The basic idea of having these interfaces is this. Whenever a call is made from the UI layer to the Business Layer, the method call would return an instance of type "IResult" or "IResultList". Within this we can indicate whether the method call succeeded or failed and incase the call has to return a value, it will be encapsulated and returned as the "Result" property of "IResult" or "IListResult".

By enforcing such a mechanism, we can ensure that we can return the appropriate message(if required) and also return the execution result. In order to do this, we create a couple of classes that derive from "IResult" and "IResultList". I'm listing out the class that derives from "IResult" below:


public class ExecutionResult<T> : IResult<T>
{

public T Result
{
get;
set;
}


public string Message
{
get;
set;
}

public MessageTypeEnum MessageType
{
get;
set;
}
}


In order to use this in a more easy manner, we create an extension method as shown below:

public static class Extensions
{
public static IResult<T> ToExecutionResult<T>(this T resultSet, MessageTypeEnum notificationMessage, string message)
{
return new ExecutionResult<T> { Message = message, Result = resultSet, MessageType = notificationMessage };
}

public static IResult<T> ToExecutionResult<T>(this T resultSet)
{
return new ExecutionResult<T> { Result = resultSet, MessageType = MessageTypeEnum.None };
}
}


With the extension method in place, let us now see how we will use this when we write code in the business classes. For sake of simplicity, I will be writing a method authenticate will succeed if both the username and password are same.

public class AuthenticatorBO : IAuthenticatorBO
{
public IResult<DTO.LoginDetail> Login(string userName, string password)
{
if (string.Equals(userName, password))
{

LoginDetail loginDetail = new LoginDetail() { Username = "Nitin", Password = "Shinde" };
return loginDetail.ToExecutionResult(MessageTypeEnum.Success, "Login Successful");
}
else
return new ExecutionResult<DTO.LoginDetail>() { Message = "Invalid Username/Password",MessageType=MessageTypeEnum.Failure };
}
}


In the next blog post(maybe next few), I will cover the remaining sections.. A simple caching mechanism,a couple of ways to handle exceptions in the Business Layer, and a simple message formatting mechanism.

Till then, happy coding n take care !

No comments:

Post a Comment