Bullet proof coding – SharePoint Error checking and Exception handling

September 14, 2008

Error and exception handling is very important in creating a robust and high quality system and more importantly for trouble shooting. Error and exception handling must be done correctly so that potential issues with the system can be diagnosed quickly.  There is nothing worst then an error being thrown and you cannot figure out what has caused the problem because the log file is showing a generic message. In addition, developers tend to develop and test their components in a manner that will always work. As soon as an end user tests or uses the system they will tend to use the system in all sorts of ways that will result in the system breaking. When this happen if the code base does not use rigorous error checking and exception handling mechanisms it becomes very difficult to diagnose the problem and gives no chance to the end user or administrator clues  on  how to rectify the issue. It will require a developer to debug the code (that is if he/she can reproduce the error in a SharePoint development environment) to understand what is actually causing the error.

Software components should be develop to a commercial quality standard and must be robust enough to except undesirable situations without breaking the system. Employing a test driven approach for unit testing your components is a good way in developing code to see the effects and characteristics of your components when undesirable situation occur. In addition, develop your system to be smart, check for undesirable circumstance and if possible rectify the problem by taking a different course of action. The following suggestions are best practices in developing code related to MOSS development in regards to error checking and exception handling. The following suggestions are a good start in formulating a template for your SharePoint code reviews.

 

The Basics

·         Always display a high level abstract message to the user when an error occurs or an exception is thrown rather than a low level exception message that the user will not understand. The low level specific error message should be written to the log file.

 

·         Check objects, values, properties etc before using them so that unexpected exceptions are not thrown.  For example

         – When obtaining an object always check the object is not null before using it.

         – Always check strings to make sure they are not empty or null i.e. String.IsNullOrEmpty(“aString”)

 

·         Never use try catch blocks to control the logic of your code, it is more efficient to use if/else statements and only throw exceptions when nothing else can be done.

 

·         When casting always check the object can be cast to the particular object. For example:

if(MyObject is String)

{

// Cast object your object

}

 

·         Always log an error when it occurs and write your log message in detail around the specific error. Don’t just log the error by calling the Message property on the exception object. Add context around the error for the specific operation it is trying to do.

 

 

·         Always catch the specific exception.  Developers tend to be lazy and catch the generic exception (i.e. System.Exception Object) around a large code block. The issue here is that although you have caught the exception it still becomes very difficult to determine what really cause the problem because the error message will be generic.

 

·         Never throw an exception inside a constructor as it can be very difficult to find where the exception is being thrown and always catch all exceptions in a Dispose method if you are defining your own.

 

·         Try/Catch blocks should never contain empty catch handlers.

 

Logging

An important part of exception handling is providing detailed information around the error in order to trouble shooting errors that may arise during development, UAT and/or Production. Generally, when developing a MOSS solution I like to use the MOSS logger (i.e. PortalLog) so as to keep all diagnostic and error information in one log file (i.e. in the 12 hive under the LOGS directory) and no extra dll’s are needed to be installed. The only limitation to using the MOSS logger is that you cannot set the log event level e.g. critical, high, medium, low it will always display the error level as high. The MOSS logger uses the PortalLog class and logs a message by calling the LogString method as shown below:

            Microsoft.Office.Server.Diagnostics.PortalLog.LogString(“log your error”);

The MOSS logger is located in the Microsoft.Office.Server.dll and therefore it is only available with the MOSS install, not WSS 3. If you want more control and flex ability use the Enterprise library logger.

SharePoint Exception handling

·         In SharePoint always throw SPException Objects to the top of the UI call stack and pass user friendly messages to the SPException object as this will be the message displayed to the user in the browser. Do not pass the Message property of the specific exception into the SPExcetption object as the user may not understand such a technically messages.

 

·         When exceptions occur in custom feature receivers catch the specific exception, log the error and throw a SPException object to the top of the UI stack as shown below.

public class MyFeatureReciver : SPFeatureReceiver

{

   public override void FeatureActivated(SPFeatureReceiverProperties properties)

   {

      try

      {

         // Do Something

      }

      Catch(ArgumentException)

      {

         PortalLog.LogString(“log Log detailed message.”);

         throw new SPException(“Enter user friendly message.”);

      }

   }

}

If you only catch and log the exception without throwing the SPException  then the  feature will complete the requested feature  activation or de-activation (which ever the user is currently requesting) instead of aborting the feature activation or de-activation.

 

·         When developing a custom feature receiver in which you are obtaining an SPSite or SPWeb object via the Parent object make sure you check if the parent object can be casted to a SPSite or SPWeb object (depending on the scope of your feature) before casting it. An example is provided below.

      public class MyFeatureReciver : SPFeatureReceiver

      {

      public override void FeatureActivated(SPFeatureReceiverProperties properties)

      {

         Object parent = properties.Feature.Parent;

         if(parent is SPWeb)

         {

            SPWeb spWeb = (SPWeb)parent;

         }

         else

        {

            PortalLog.LogString(“log Log detailed message.”);

            throw new SPException(“Enter user friendly message.”);

         }

       }

      }

 

·         When developing webparts which have custom user defined properties available in the web part editing pane, the webpart should always check the value and if it is not valid display an error message in the web part. This applies to the situation when a user changes the value in the web part editing pane. If the value is invalid the user should be notified after they click apply, so they can immediately change the value to a correct value.

 

 

·         When developing web parts that depend on data from a SPLists you should always catch the UnauthorizedAccessException object that will be thrown if the user does not have permissions. Web parts will be accessed by many different people with different permissions and in some situations users may not have the required permissions for particular lists. The developer may want to catch the exception and display a message in the web part or implement some type of action i.e. display a request for access control. In addition, you will also have to set SPSecurity.CatchAccessDeniedException = false so that the platform does not catch the access denied exception. 

 

·         When accessing lists (SPList) or list items (SPListItem) always catch the appropriate exception which will be thrown if the list or list item does not exist. If you access a list a list or list item via the collection key and the list or list item does not exist an ArgumentExecption will be thrown. This is illustrated below.

 

try

{

   spList = spWeb.List[“My WebSite”];

   // or

   spListItem = spList.Items[“My Item”];

}

catch(ArgumentException)

{

   // Log and take appropriate action for the exception

}

 

If you access a list or list item via the relative server URL and the list or list item does not exist a FileNotFoundExecption object will be thrown . This is illustrated below.

try

{

   spList = spWeb.GetList(“/Lists/MyList”);

   // or

   spListItem = spWeb.GetListItem(“/Lists/MyList/MyItem”);

}

catch(FileNotFoundException)

{

   // Log and take appropriate action for the exception

}

 

Otherwise, in some situations you may want to check if the list item exists before accessing it, an example is shown below.

 

if(spList.Fields.ContainsField(“MyItem”);

{

   spListItem = spList.Items[“My Item”];

}

else

{

   // Take appropriate action

}

 

However if you want to check if a list exists you will have to enumerate through the  web site’s Lists collection and check if it exits, there is no method in the Object model that does this so you may want to extend the SPList class to accommodate this.

 

·         Never dispose of SPSite or SPWeb object inside of a try block, if an exception gets thrown before the Dispose method it will not dispose of the object appropriately, leading to memory leaks,. Best practice is to wrap your code in a using block when creating these objects, for example

Using (SPSite site collection = new SPSite(http://localhost)

{

   // Do something

}

 

·         When checking out items make sure that your code can roll back changes or check the file back in when an exception occurs. Otherwise the item will remain in a checked out state and users will not be able to use that particular file. For example

try

{

   file.CheckOut();

   // Do something to the file

   File.CheckIn(“comment”);

}

finally

{

    if (file.CheckOutStatus != SPCeckOutStatus.None)

           file.UndoCheckOut();

}

 

 

Burlo

 

 

 

 


Follow

Get every new post delivered to your Inbox.