Event Receivers in SharePoint 2010

You might ask what is new with Event Receivers in SharePoint 2010 and you are sure to get an answer that it is now possible to have event receivers for:

  1. List
  2. Workflows

event_receivers

Synchronous and Asynchronous Event Receivers

Now in SP2010, you can configure event receivers to be:

  1. Synchronous
  2. Asynchronous

By default event receivers are asynchronous meaning that they run under the SharePoint timer service (OWSTIMER.exe). This means that the event receivers:

  1. Are not executed immediately, rather it is scheduled to be executed by OWSTIMER.exe.
  2. Don’t run under w3wp.exe process anymore, so when you want to debug your event receivers, you have to attach to OWSTIMER.EXE and wait till they are executed.

However, you can control this behaviour using the Synchronization attribute:

synchronization_attr 

If you set it to Synchronous, then the event receivers run immediately and under the w3wp.exe process. So, if your solution depends on event receivers highly and expect the results immediately, then you have to set it to Synchronous, else setting it to Asynchronous should do the job.

ItemUpdated event happening twice when you force Check Out option

Well, today at work, I was really confused why ItemUpdated event in the Document Library was getting called twice. Debugging, it appears that the ItemUpdating event or the ItemUpdated event occurs two times when you enable the Require Check Out option for Document Library! - Thats bad, this was increasing the database requests that we were doing in the ItemUpdated event!

So, Here is THE Solution if you don't want the ItemUpdating and ItemUpdated event get called twice! - http://support.microsoft.com/kb/939307

To work around this behavior, examine the vti_sourcecontrolcheckedoutby property inside an event receiver. If the vti_sourcecontrolcheckedoutby property exits in the BeforeProperties property but not in the AfterProperties property, the event was caused by checking in a document. The following sample code shows you how to do this.

if (properties.AfterProperties["vti_sourcecontrolcheckedoutby"] == null && 
properties.BeforeProperties["vti_sourcecontrolcheckedoutby"] != null)
{
//This is when the update event is triggered by check-in.
}
else
{
//This is triggered by events other than check-in action.
}

Feature Receivers and SPContext.Current and SPFeatureReceiverProperties

Another day, another discovery! – MOSS Rocks! :)

I had to create a List using a Feature. So, I added my code in the FeatureActivated method. The first thing I did was to get the current web (site)

public override void 
FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb CurWeb = SPContext.Current.Web;
// Rest of the code follows....
}

 

I went ahead and installed the feature using stsadm command. It worked fine. I then activated the feature and ended up getting this error!

Object reference not set to an instance of the object

If I remove the code inside the FeatureActivated method, it works! And, If I activate the feature from the web browser, it works!

Googling, I found that you cannot use SPContext.Current inside FeatureActivated when using stsadm (via command prompt) to activate the feature

Why? – Very simple, cmd.exe process does not understand or know how to get your current context! If you are running via browser, you have the w3wp.exe process from which you can get the context!

 

So, is there a way I could get the Web or Site to which the feature is getting deployed? Of course, YES!

Below is an extension method for you! (from here - Matt Smith’s blog, who is now enjoying his vacation!):

public static class Extensions
{
/// <summary>
/// Gets the web.
/// </summary>
/// <param name="properties">The properties.</param>
/// <returns></returns>
public static SPWeb 
GetWeb(this SPFeatureReceiverProperties properties)
{
SPWeb site;
if (properties.Feature.Parent is SPWeb)
{
site = (SPWeb)properties.Feature.Parent;
}
else if (properties.Feature.Parent is SPSite)
{
site = ((SPSite)properties.Feature.Parent).RootWeb;
}
else
{
throw new Exception("Unable to retrieve SPWeb - 
this feature is not Site or Web-scoped.");
}
return site;
}
}

Depending on the scope of your feature, you cast the Parent.

And you use it like this:

public override void 
FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb CurWeb = properties.GetWeb();
Guid listId = CurWeb
.Lists
.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
CurWeb.Update();
}

SharePoint - Setting Item level permission

Back at work, I had a requirement to set item level permission in a SharePoint Document Library. What does this mean? Below is a ‘diagram’

image

Diagram 1

It is very simple to explain in simple words – I want to set unique permissions on each file in my document library.

And the same in SharePoint words – I want to set unique permissions on a ListItem (SPListItem) which resides in a List (SPList) which again resides in a SharePoint Web Application (SPWeb).

The file resides inside a ListItem and thus you need to access the file via its ListItem. Using site settings, you can configure the default permissions for the document library. But there is no option to manage the default permission level for a ListItem. Whenever a new item is added to the document library, SharePoint adds the parent’s permission levels down to the child – i.e. document library’s permission levels to the list item – This is called Role Inheritance (Blue Line in the Diagram 1). So, if you want to add unique permissions to a list item, you need to first break this role inheritance and then add unique permissions.

The only way to achieve this is to write code. I had two options :

1) Handle adding unique permissions in any of the document library event handlers (of your choice)

2) If you have separate application dealing with adding items to the document library, then you can directly handle permissions in that application

Well, I needed both because I had to set permissions on the item when it was manually added to the library using the SharePoint website and also via the application :(

I started to investigate which event handler to use. Since I had to access item’s properties(fields), I had to choose ItemUpdated event handler. Why? Below is the ‘event sequence’ that happens when you upload a document in the document library:

image

Diagram 2

When a document is uploaded, first the item is added and then checked in. Once the item is checked in, SharePoint allows you to provide values to the custom fields that you may have for your item. Even for the default fields SharePoint adds them once the item is checked in. I am not sure how useful the ItemAdding event is as I was not able to even retrieve the item that was being added in that event. But once the item is added, I got the freedom to set permissions but still I was not able to grab the field values that user may enter, as the item is not yet checked in and the user is not presented with the input screen. Once the item is checked in, now the user gets the screen to input the field values and when the user clicks OK, the ItemUpdating and ItemUpdated events fire up!

That was when we use the SharePoint web UI.

If you use code to add an item to the document library, things happen when you add and then perform an update on the item.

Now remember that since I am using an event handler to set permissions, even If I perform an update via the code, my event handler will fire up, which is great!

This was easy : Wrote my ItemUpdated event handler, set the permissions. As I said earlier I had to break the role inheritance and only then set permissions on the item. Great! I was ready to test this. I had a separate application (web service) which deals with uploading files to the document library. I had a count of 600 files in the initial run to upload.

I hit the button and the application started uploading files..

first 50 – super fast, excellent

next 100 – fast, Excellent :D

next 100 – fast, Excellent :D

next 100 – slow, OK :-/

next 10 – dead slow, NOT OK :-/

next …. – dead slow as it was still dealing with the above 10 items :( :(

And as this upload was going on, our DBA guys came running to me (as they know am dealing with SharePoint database) saying that the CPU usage in the SharePoint database machine is 100% and the database transaction log is growing exponentially high! I was really confused and told them to wait until my 600 files were uploaded. After fifteen minutes I had to stop my application as the disk space where the database logs + database resides became full :(

My team was very furious now. My small application brought the database server down!  DBA guys had set up only 5GB of space to the whole SharePoint database (That’s more than enough for our SharePoint Web Application) and they had enabled full transaction logging on the server. The logs were getting bigger and bigger that it went up to 2GB!

What was happening?

It took time but we eventually found out what was happening! The DBA guys were helpful in telling us about a stored procedure that was being called most of the time and also those calls being ‘threaded’ calls (multiple threads). Sample calls below:

WSS_Content_Workspace.dbo.proc_GetTpWebMetaDataAndListMetaData 
WSS_Content_Workspace.dbo.proc_SecGetRoleDefskdc-dmz-consume-SharePoint-Config.dbo.proc_CompleteTimerRunningJob
WSS_Content_Workspace.dbo.proc_SecRemovePrincipalFromScope
WSS_Content_Workspace.dbo.proc_GetTpWebMetaDataAndListMetaData
WSS_Content_Workspace.dbo.proc_GetTpWebMetaDataAndListMetaData

That helped to us to start investigating with setting the permissions. So, there were two places:

1) Breaking the role inheritance

2) Setting manual permissions

We have two options when we break the role inheritance which is show below

image

You can choose to either copy the document library permissions’ to the item or not. Does this make a big difference? Well, YES!

If you choose to copy the permissions down to the item, then SharePoint copies the permissions to the item and does an item update! This makes our event handler to get invoked and that again sets permissions and then again my code has an item update after setting the field values, again, my event handler gets invoked and sets permissions. Unfortunately my code had set to copy the permissions down to the item :( And remember, the event handlers are asynchronous and thus those ‘threaded’ calls! Just think this for 600 files!

Now, you might have another question – how come copying down the permissions slowed things down ?

When we set unique permission to an item, SharePoint copies that permission back to the document library with ‘Limited Access’ permission level. SharePoint does this because it is not smart enough to find whether that unique permission already has access to the document library. SharePoint even goes beyond in adding this unique permission to the site as again SharePoint is not smart enough to find whether that unique permission already has access to the site.(Red Line in the Diagram 1) Why? Unless you have access to site, you cant access the document library and unless you have access to the library, you cant access the item! This goes on for each item, so at the 201st item we would have 200 unique permissions added to the document library and site. So, when we break the role inheritance with copy permissions set true – SharePoint will copy all those 200 permissions to the newly created item! We are now forced to remove those permissions and set our new permissions! That’s bad and that is what this blog post uses !  Don’t even try removing that permission level added to the document library because SharePoint is smart enough to remove the permission on any item inside that document library, thinking that that permission does not need to have access to the items!

So, the proper code to use when you set item level permission and break role inheritance would be :

if (!item.HasUniqueRoleAssignments){    item.BreakRoleInheritance(false);}

 

 

This does not copy down the permissions to the items :D

One small flag caused us a lot of trouble and you wouldn’t even notice this CPU hike, logs usage etc., unless you have a very restricted environment where everything is limited! I don’t want to get into various other problems that we faced as a consequence of this, but do not forget to really analyze your code step by step before you completely blame SharePoint ;)

Any links for good practices?

1) Common coding issues when using the SharePoint object model

2) Using Disposable Windows SharePoint Services objects

3) Roger Lamb’s cool posts about SharePoint 2007 and WSS 3.0 Dispose Patterns with examples


Creative Commons License
Chaks' Corner Blog by Chakkaradeep Chandran is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
Based on a work at www.chakkaradeep.com.
Permissions beyond the scope of this license may be available at http://www.chakkaradeep.com.