Click here to monitor SSC
  • Av rating:
  • Total votes: 30
  • Total comments: 3
Damon Armstrong

Managing ItemUpdating and ItemUpdated Events Firing Twice in a SharePoint Item Event Receiver

17 January 2012

Developing a Sharepoint application would have all the fun of a video game, if only you had infinite lives. Dangers lurk hidden out there which, if you run into them, can be a blow to your project and waste a great deal of time. Damon gives just one example of a poisoned dagger in the game of Sharepoint Development: The Item Event Receiver.

I’m usually disappointed when writers employ oft-overused metaphors to describe a situation. With that in mind, SharePoint 2010 is like a sea of icebergs – there is a lot going on under the surface that you may not notice until it’s too late. Unfortunately, that makes your project like the Titanic. I don’t mean that it’s largest and most luxurious application every written, but rather that you may be cruising headlong into a nasty rendezvous with an iceberg that could deal a severe blow to your project. We may never know about all of the dangers lurking out there, but today we’re going to cover at least one danger you may encounter while writing event receivers – an annoying issue with the ItemUpdating and ItemUpdated events firing twice. I should also point out that I know the difference between a metaphor and simile in case that was bothering you from the opening sentence. I am nothing if not a masterful linguist after a beer or two or more.

What is an Item Event Receiver?

Hopefully you know about item event receiver if you are having problems with them firing twice. If not, kudos to you for tackling the object model with reckless abandon. Sometimes that is the most exciting way to learn, but for those less adventurous I will briefly cover the topic here. You can think of an item event receiver like a database trigger: it has different events that fire during the course of SharePoint running an operation on a list item (or document item).

Event Name

Synchronous

Description

Item Events

ItemAdding

Yes

Runs before an item is added to the list.

ItemAdded

 

Runs after an item has been added to the list.

ItemDeleting

Yes

Runs before an item is deleted from the list.

ItemDeleted

 

Runs after an item has been deleted from the list.

ItemUpdating

Yes

Runs before an item is updated in the list.

ItemUpdated

 

Runs after an item has been updated in the list.

Check-In Events

ItemCheckingIn

Yes

Runs before an item is checked in.

ItemCheckedIn

 

Runs after an item has been checked in.

ItemCheckingOut

Yes

Runs before an item is checked out.

ItemCheckedOut

 

Runs after an item has been checked out.

ItemUncheckingOut

Yes

Runs before an item is un-checked out.

ItemUncheckedOut

 

Runs after an item has been un-checked out.

File Events

ItemFileConverted

 

Runs before a file in a doc library is converted to a different file type.

ItemFileMoving

Yes

Runs before a file is moved.

ItemFileMoved

 

Runs after a file has been moved.

Attachment Events

ItemAttachmentAdding

Yes

Runs before an attachment is added to an item.

ItemAttachmentAdded

 

Runs after an attachment has been added to an item.

ItemAttachmentDeleting

Yes

Runs before an attachment is deleted from an item.

ItemAttachmentDeleting

 

Runs after an attachment has been deleted from an item.

Item Event Receivers derive from the SPItemEventReceiver class and have a number of methods that can be overridden to respond to various events:

As you look through this list, you should notice that events have two types of endings:

  1. Events ending in ING:
    • Examples include ItemAdding, ItemDeleting, ItemUpdating, etc.
    • These events occur BEFORE the action takes place. This means that you have a chance to modify the item or cancel the operation before it occurs.
    • These event handlers run synchronously – they occur in order and one must complete before the next is run.
    • You may be able to modify property values in the event handler.
      • Each event method has a SPItemEventProperties parameter named properties. If you wish to modify a property value on the list item during the event, the value should be updated in AfterProperties property of the properties parameter. SharePoint reads these values from the event parameter and modifies the item accordingly when the actual operation runs (e.g. the actual Add / Update operation for which the Adding / Updating event is being fired). Do NOT try to manually get the list item in code and update a property on it because the optimistic locking mechanism in SharePoint may throw an error later on when the operation associated with the event to which you are responding attempts to complete.
  2. Events ending in ED:
    • Examples include ItemAdded, ItemDeleted, ItemUpdated, etc.
    • These events occur AFTER the action takes place. This means that you can respond to the event but you cannot cancel it or modify anything about it.
    • These events handlers may run synchronously or asynchronously depending on how they are configured. Asynchronous operations may occur in any order and complete in order.
    • Property modification is not available in an asynchronous event handler (even if it is running synchronously).
      1. Although asynchronous events expose a SPItemEventProperties parameter named properties just like their synchronous counterparts, remember that the operation has already completed so you cannot modify anything in the properties parameter (well, you can, but it doesn’t do anything). Additionally, the properties parameter may not be populated with information that you would tend to expect to be present.

WARNING: One major gotcha you should know about the SPItemEventReceiver class is that while you can implement multiple list item event handlers in a single class, SharePoint instantiates a new instance of that class for each individual event it needs to handle. What this means is that you cannot store data in instance-level variables and share that data between event handlers. For example, if you define an instance level variable in the class to store data in the ItemUpdating event, then try to access that data in the ItemUpdated event, you will find that the data is not there when you go to check it in the ItemUpdated event. This is because you have two classes – one that is handling the ItemUpdating event and in which the instance level variable is set, and one that is handling the ItemUpdated event in which the instance level variable is not set.

When Do the ItemUpdating and ItemUpdated Events Fire Twice?

Simply put, the ItemUpdating and ItemUpdated fire twice when adding a document to a library that has the Require Check Out option enabled. To understand why this is happening, let’s first look at what happens when the user adds a document to the library when the Require Check Out option is disabled:

  • User clicks on the Add a Document link
  • SharePoint displays the Upload a Document dialog
  • User uploads the Document to SharePoint
  • SharePoint fires the ItemAdding Event
  • SharePoint adds the document to the Library
  • SharePoint fires the ItemAdded event
  • SharePoint displays the file in the library

So the net result of this is that the document is uploaded and the ItemAdding and ItemAdded events have fired, which is pretty much what you would expect. Next, let’s look at what happens when the user adds a document when the Require Check Out option is enabled.

  • User clicks on the Add a Document link
  • SharePoint displays the Upload a Document dialog
  • User uploads the Document to SharePoint
  • SharePoint fires the ItemAdding Event
  • SharePoint adds the document to the Library
  • SharePoint fires the ItemAdded event
  • SharePoint displays the property editing screen to the user
  • User edits any properties they wish to change and clicks the Save button
  • SharePoint fires the ItemUpdating event (first time it is fired)
  • SharePoint sets any document property field entered in the property editor dialog
  • SharePoint fires the ItemUpdated event (first time it is fired)
  • SharePoint now begins the process of automatically checking in the document
  • SharePoint fires the ItemUpdating event (second time it is fired)
  • SharePoint fires the ItemCheckingIn event
  • SharePoint fires the ItemUpdated event (second time it is fired)
  • SharePoint fires the ItemCheckedIn event

The first time the ItemUpdating and ItemUpdated events fire it is in response to the document properties changing. The second time they fire it is in response to the document being checked in. It appears as though they are firing twice in this situation because SharePoint is updating the properties on the document and then checking it in on the same request. If you were to check the document out and edit the properties on the document, you would see the ItemUpdating and ItemUpdated events fire once. Later on, when you checked the document in, you would see those events fire again. So the double-event firing isn’t a bug, it’s just a result of the automatic check-in that occurs when you first add a document to a document library.

Note: when the property editor dialog displays, the user has the option to cancel out of the dialog. If the user opts to cancel, then only the ItemAdding and ItemAdded events will have fired and the document will be left in a checked out state.

Also note: the ItemUpdating and ItemUpdated events that fire in response to the properties being edited from the dialog will always occur, even if the user is not entering or changing any of the values.

Turning Off the Require Check Out Option

One option for fixing the double-event firing issue is to simply turn it off. You can locate the setting using the following instructions:

  • Click on a list in the left navigation menu (or otherwise get into a list)
  • Click the Library tab in the Ribbon
  • Click Library Settings button in the Library Tab
  • This will take you to the list information screen
  • Click the Versioning settings link
  • The Require Check Out radio button should appear in the list of settings

Programmatically Determining the Cause of ItemUpdating and ItemUpdated Firing

Turning off the Require Check Out option is a great quick fix if you don’t require the item to be checked out in order for it to be edited. But that option exists to be used, and some people really do need it. If you find yourself in this situation, then you’ll have to solve the problem in code. Fortunately, there is a relatively simple way to check whether the ItemUpdating and ItemUpdated events are firing in response to a check-in outlined in Knowledgebase Article 939307. You just have to check to see if the vti_sourcecontrolcheckedoutby property on the item was cleared:

public override void ItemUpdating(SPItemEventProperties properties)
{
    
if (properties.AfterProperties["vti_sourcecontrolcheckedoutby"] == null
        
&& properties.BeforeProperties["vti_sourcecontrolcheckedoutby"] != null)
    
{
        
//Code to run if event trigged by a check-in
    
}
    
else
    
{
        
//Code to run if event trigged by something else
    
}
}

This code is using the BeforeProperties and AfterProperties on the properties parameter to see what the value of the vti_sourcecontrolcheckedoutby property on the item was before the update occurred, and what it will be after the update has completed. The vti_sourcecontrolcheckedoutby property identifies who the item is currently checked-out to. As such, if the item is being checked-in, the BeforeProperties will contain a value for the property and the AfterProperties will not. It’s a pretty simple fix, but we can definitely make it a bit more reusable for everyone on a development team and reduce the hassle of having to remember the specifics about how to run the check in their ItemUpdating and ItemUpdated event handlers.

Creating a Base Class for Event Receivers

Following is the code for a base class that adds a new parameter to the ItemUpdating and ItemUpdated methods that specifies whether the event was called as a result of a check-in operation.

/// <summary>
///   Base class for ItemEventReceivers that helps developers determine if the ItemUpdating
///   and ItemUpdated events were thrown as a result of a Check-In operation.  Please see
///   http://support.microsoft.com/kb/939307 for more information.
/// </summary>    
public abstract class SPItemEventReceiverBase : SPItemEventReceiver
{
    
/// <summary>
    ///   Contains the SharePoint field name checked when determining if the item was checked-in.
    /// </summary>
    
private const string SourceControlFieldName = "vti_sourcecontrolcheckedoutby";


    
/// <summary>
    ///   Runs when the ItemUpdated event is raised.
    /// </summary>
    /// <param name="properties">SharePoint event properties.</param>
    /// <param name="isCheckIn">Contains a boolean value indicating whether the method is being run in response to a check-in operation.</param>
    
public virtual void ItemUpdated(SPItemEventProperties properties, bool isCheckIn)
    
{
    }


    
/// <summary>
    ///   Runs when the ItemUpdating event is raised.
    /// </summary>
    /// <param name="properties">SharePoint event properties.</param>
    /// <param name="isCheckIn">Contains a boolean value indicating whether the method is being run in response to a check-in operation.</param>
    
public virtual void ItemUpdating(SPItemEventProperties properties, bool isCheckIn)
    
{
    }


    
/// <summary>
    ///   Determines whether the ItemUpdated event has been raised as a result of a check-in
    ///   operation and then passes that information to the overloaded ItemUpdated method.
    /// </summary>
    /// <param name="properties">SharePoint event properties.</param>
    
public sealed override void ItemUpdated(SPItemEventProperties properties)
    
{
        
if (properties.AfterProperties[SourceControlFieldName] == null &amp;&amp; properties.BeforeProperties[SourceControlFieldName] != null)
        
{
            ItemUpdated
(properties, true);
        
}
        
else
        
{
            ItemUpdated
(properties, false);
        
}            
        base.ItemUpdated
(properties);
    
}


    
/// <summary>
    ///   Determines whether the ItemUpdated event has been raised as a result of a check-in
    ///   operation and then passes that information to the overloaded ItemUpdated method.
    /// </summary>
    /// <param name="properties">SharePoint event properties.</param>
    
public sealed override void ItemUpdating(SPItemEventProperties properties)
    
{
        
if (properties.AfterProperties[SourceControlFieldName] == null &amp;&amp; properties.BeforeProperties[SourceControlFieldName] != null)
        
{
            ItemUpdating
(properties, true);
        
}
        
else
        
{
            ItemUpdating
(properties, false);
        
}
        base.ItemUpdating
(properties);
    
}

}
//class

So, let’s talk about the code for a second. You should notice that we’ve added two new methods:

  • public virtual void ItemUpdating(SPItemEventProperties properties, bool isCheckIn)
  • public virtual void ItemUpdated(SPItemEventProperties properties, bool isCheckIn)

These methods are just like the ItemUpdating and ItemUpdated methods in the SPItemEventReceiver class, but they have an additional Boolean parameter named isCheckIn that indicates whether or not the event is being raised as result of a check-in operation. They are declared as virtual methods and have empty method bodies because we want developers to override them in a derived class.

Next, we have overridden the standard ItemUpdating and ItemUpdated methods defined in the SPItemEventReceiver class. In both methods we make the check to see if the vti_sourcecontrolcheckedoutby item property has been cleared out and then call our new ItemUpdating or ItemUpdated method with the appropriate Boolean value to indicate whether the event was called as a result of a check-in operation. Notice that both of these method are marked with the sealed keyword. This ensures that developers in a derived class do not accidentally override this method instead of the new one.

Using the Base Class

To use the base class in your project, just have your Item Event Receiver classes inherit from SPItemEventReceiverBase instead of SPItemEventReciver. Then override the new ItemUpdating or ItemUpdated methods and use the isCheckIn parameter to ensure you don’t run your event receiver code more than you need to run it.

public override void ItemUpdating(SPItemEventProperties properties, bool isCheckIn)
{
    
if (isCheckin)
    
{
        
//Code to run if event trigged by a check-in
    
}
    
else
    
{
        
//Code to run if event trigged by something else
    
}
}

Warning: Do not call base.ItemUpdating(properties) or base.ItemUpdated(properties) from inside an overridden IteamUpdating or ItemUpdating method when using the SPItemEventReciverBase. This will cause an infinite recursion loop and instead of solving the “It’s firing twice problem” you will now have an “It’s firing 1000 times problem.”


ANTS Performance Profiler How does your SharePoint application perform? The Beta release of ANTS Performance Profiler 8 adds support for SharePoint 2013, helping rapidly identify and understand SharePoint performance issues. Try the Beta.

Damon Armstrong

Author profile:

Damon Armstrong is a Senior Engineering Team Lead with GimmalSoft in Dallas, Texas, and author of Pro ASP.NET 2.0 Website Programming. He specializes in Microsoft technologies with a focus on SharePoint and ASP.NET. When not staying up all night coding, he can be found playing disc golf, softball, working on something for Carrollton Young Life, or recovering from staying up all night coding.

Search for other articles by Damon Armstrong

Rate this article:   Avg rating: from a total of 30 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: question about async/synch for events ending in ED
Posted by: c1sbc (view profile)
Posted on: Tuesday, January 24, 2012 at 8:20 AM
Message: In the above text -
"2.Events ending in ED: d.Property modification is not available in an asynchronous event handler (event if it is running synchronously)."

should it read - "in an asynchronous event handler (EVEN if it is running synchronously)." ?

Subject: c1sbc: Yes, it should.
Posted by: Damon (view profile)
Posted on: Tuesday, January 24, 2012 at 2:50 PM
Message: Good catch. It should say "even if" -- I will let the Simple-Talk.com editors know about this to see if they can get it resolved. Thanks!

Subject: typo
Posted by: Andrew Clarke (view profile)
Posted on: Wednesday, January 25, 2012 at 10:19 AM
Message: Oops! fixed.

 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

TortoiseSVN and Subversion Cookbook Part 10: Extending the reach of Subversion
 Subversion provides a good way of source-controlling a database, but many operations are best done from... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.