65.9K
CodeProject is changing. Read more.
Home

Message Maintenance Architecture with C# and XML

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.38/5 (6 votes)

Mar 4, 2005

4 min read

viewsIcon

34485

downloadIcon

2

De-coupling Error Messages out of your web application.

Overview

In today’s IT world "Error Messages" or "Information Messages" in any systems are inevitable. In application development the messages are normally coupled into the system. So, whenever the customer asks for changes in messages, developers get into the code and change the messages, which is generally error prone. When trying to change the messages there are possibilities that new bugs may get introduced.

With the power of .NET and XML, it is possible to de-couple the messages outside the system and it is easy for maintenance. Whenever customer asks for change in messages, they themselves can go and change the messages in the XML repository or it is easy for any one who knows to operate the computers to change the messages without shutting down the application.

Let us have a look how we can achieve it with .NET and XML.

Brief Architecture Description

All the messages of the system are stored in the form of XML with unique ids. We can have a common method/function to retrieve the messages by passing their respective ids. In this way we can de-couple the messages outside the system. With powerful caching features available in .NET, we can cache the XML file and set a dependency to it. So, for the first request the XML file is cached in to the server’s memory and the subsequent requests will read from the cached object and service the client requests. We can even use resource files, but changing the resource files again needs to compile. Also, changing the XML doesn’t need restart of the server or application since it has cache dependency. So, whenever customer needs a change in messages he can just change the text in XML and the subsequent requests by the client to that particular message will be served with new messages.

Detailed Functional Description

1. XML Part

Let us have a look at the format of the XML.

<?xml version="1.0" encoding="utf-8" ?>
<!--
1. Message Id should be maintained Unique. It will 
be a 3 Digit numeric and sequential.
2. Try to avoid "/", "\" and "<",">". Instead use "&lt;" for "<" etc.
--> 
<Messages>
 <Message>
  <MsgId>100</MsgId>
  <MsgValue>Please Enter Project Name &lt;MSG&gt;</MsgValue>
 </Message>
 <Message>
  <MsgId>101</MsgId>
  <MsgValue>Please Enter Project Date</MsgValue>
 </Message>
</Messages>
The XML file has root tag of "Messages". It has "Message" node, which has "MsgId" and "MsgValue" as its child nodes. Basically, these nodes have information about the Messages. Message Id (MsgId) is maintained as 3 digits, so that the first digit represents Module Id and the other 2 digits represents the Message. Number of digits can be changed depending upon the number of messages, Also it may have any combination of characters to be uniquely identified. For example, "MOD-001".

The "MsgValue" node contains the actual message to be displayed. The message should not have some XML reserved characters like "<" or ">" etc. If these characters or to be included in the message try give the HTML Encode value for it or add XML escape characters.

For example, Message like "Password can’t be blank" can be given as "Password can\’t be blank".

Have a look at the Message Value of Id 100 in the XML. The message has "&lt;MSG&gt;". This is a pattern given in the message which can be replaced and used for general messages. For Example. If you have messages that change only a part of sentence can be put as same and while calling the method/function, we can pass the value as parameter.

Consider these messages, "Please enter Project Name" and "Please enter Project Description". We can have single message in the XML like "Please enter Project &lt;MSG&gt;" and we can call it as GetMessage("100","Name") to display first message and GetMessage("100","Description") for second message. On seeing the code we will understand how it is possible.

2. C# Part

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.Caching;
using System.Collections.Specialized;
using System.Xml;
using System.Xml.XPath;
using System.IO;

namespace CacheExample
{
    /// <SUMMARY>
    /// Summary description for ErrorMessage.
    /// </SUMMARY>
    public class ErrorMessage
    {
        public ErrorMessage()
        {
            //
            // TODO: Add constructor logic here
            //
        }
// Function Name            : (+) getErrorMsg
// Date                     : 5th March, 2005.
// By                       : Saravanan R Achari
// Purpose                  : To Retrieve the Error Message 
//                            from the Corresponding Modules (generalised).
// Input Parameters         : Expects a string denoting 
//                            the Error Message Required.
// Output Parameters        : Gives the Desired Error Message.
// Environmental Constraints: Expects the XML file having error messages.

    static public string GetMessage(string intErrId,string strOptErrMsg)
    {
      // String Dictionary to hold Cached Error Messages
          StringDictionary objStrDictionary    = new StringDictionary();    
      // object to Load XML document
      XmlDocument objXmlDocument        = new XmlDocument();        
      // to get XML Node list in the specifed Xpath
          XmlNodeList objNodeList;            
      string strXPath        = string.Empty;
      string strChildXPath    = string.Empty;
       string strFilePath    = string.Empty;    
      string strErrDescription= string.Empty;
       HttpContext objContext    = HttpContext.Current;
            
     try
     {
       strFilePath = objContext.Server.MapPath("ErrorMsg.xml");
            if (objContext.Cache["strdictErrorMsg"]== null)
        {
        strXPath = "Messages/Message";
        objXmlDocument.Load(strFilePath);
        objNodeList = objXmlDocument.SelectNodes(strXPath);
        foreach(XmlNode objNode in objNodeList)
        {
            objStrDictionary.Add(objNode.SelectSingleNode(
             "MsgId").InnerText,objNode.SelectSingleNode("MsgValue").InnerText);
        }
        CacheDependency dependency = new CacheDependency(
         objContext.Server.MapPath("ErrorMsg.xml")); 
        objContext.Cache.Insert("strdictErrorMsg", objStrDictionary,dependency);
         } // if ends here
         else
          {    
        objStrDictionary = (StringDictionary)objContext.Cache["strdictErrorMsg"];
          } // else ends here
        
          if(objStrDictionary.ContainsKey(intErrId))
        {
          strErrDescription = objStrDictionary[intErrId];
          if (strOptErrMsg != string.Empty)
           {
             strErrDescription = strErrDescription.Replace("<MSG>",strOptErrMsg);
           }
          return strErrDescription;
        }
        else
        {    
         strErrDescription="";
         return strErrDescription;
        }
                
    } // try block ends here
    catch (Exception ex)
    {
      return "Invalid Error Id / Module Name";
    } // Catch block ends here
            
     }
  }
}

In the C# code above, the high lighted code is important as it puts the StringDictionary object in to server’s cache, so that XML parsing needs not to be done for each request.

"StringDictionary" object is used as it is more efficient than "HashTable" as our collection contains only strings. The "ErrorMsg.XML" is the XML file mentioned above. Keep it in the application path so as for the above source code to work; else path should be changed in the code. "GetMessage" method accepts 2 parameters. The first Parameter is the MessageId and Second Parameter is the optional message to be shown with the required message. This scenario is discussed above. For general call the second parameter will be "string.Empty".

VB.NET users can convert the above code to VB.NET.

3. ASPX Part

Even JavaScript Messages in ASPX pages can be moved to this XML file. You can access the messages as shown in script below.

<script language="javascript">
function test()
{
 var xyz = '<%=ErrorMessage.GetMessage ("101","")%>';
 alert(xyz);
}
</script>
Ensure the relevant namespaces are imported in the ASPX file.

For Example. Here my Namespace is

<%@ Import Namespace="CacheExample"%>

Conclusion

You can enhance the above code for reusability. Certain customizations can be made like passing more than one custom message to the function and appropriately servicing the client etc. Hope this article helps you. Kindly send your clarifications and queries.