Friday, April 13, 2012

SharePoint Error and Exception handling

In this blog I will provide some basics, how we can make our SharePoint code more robust and proofing against any error or exception.

The Basics

1.     Always keep null checking

2.     Always log an error when it occurs and write your log message in detail around the specific error.

3.     Always catch the specific exception.  Avoid catch the generic exception (i.e. System.Exception Object, I know we developer do this very often). 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.

4.     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.

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

6.     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 SharePoint (MOSS and SP2010) solution I like to use the SharePoint logger (i.e. PortalLog) so as to keep all diagnostic and error information in one log file (i.e. in the 12/14 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 we 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:

catch (Exception ex)

            {

                Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Exception Occured in Left Menu - Control :" + ex.Message);

            }

 

The MOSS logger is located in the Microsoft.Office.Server.dll. If you want more control and flex ability use the Enterprise library logger.

 

SharePoint Exception handling

1.      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. Below is an example within a receiver, catch the specific exception, log the error and throw a SPException object to the top of the UI stack as shown below

public override void FeatureActivated(SPFeatureReceiverProperties properties)

        {

            try

            {

                // Do Something

            }

            catch (ArgumentException ex)

            {

 Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Exception Occured in Left Menu - Control :" + ex.Message);

               throw new SPException("Check Left Menu or Left Menu String");

            }

        }

 

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.

2.     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 override void FeatureActivated(SPFeatureReceiverProperties properties)

        {

            try

            {

                Object parent = properties.Feature.Parent;

                if (parent is SPWeb)

                {

                    SPWeb spWeb = (SPWeb)parent;

                }

                else

                {

                  PortalLog.LogString("Log exception");

                  throw new SPException("Enter user friendly message");

                }

            }

            catch (ArgumentException ex)

            {

               Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Exception Occured in Left Menu - Control :" + ex.Message);

             throw new SPException("Check Left Menu or Left Menu String");

            }

        }

3.     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.

4.     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.

 

5.     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.

 

6.     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

}

7.     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();

 }

If you have a more elegant solution – please post a comment… I’ll be happy to hear.
...HaPpY CoDiNg

Partha (Aurum)

No comments:

Post a Comment