Friday, February 22, 2008

MOSS/SharePoint ItemUpdated() & ItemUpdating Events Not Picking Up Content Type Metadata

Overview:
We recently hit a funny on an Event Receiver in MOSS. It's unconfirmed as to wether it's a bug or not but in my opinion is. It was specifically related to the ItemUpdated() event not finished updating content type values when the event is fired (so effectively the item has not updated when the term "ItemUpdated" implies that it has).
Here is the MSDN Forums discussion related to this.

Business Requirements:
- Have a Document Library with differing content types attached (CTypeA, CTypB, etc.)
- Allow users to create new items in the doc lib for each content type
- Depending on the content type update certain content type fields when the file is saved or edited. For example if it is a "Leave Application" then update the "NumDaysLeft", if it's an "Expense Form" then update the "Total Expenses" field.

What Was Done:
An event was attached to the EventUpdated() event to update the relevant fields. There was code sitting in this event that checked the Content Type and would react accordingly:

if (properties.ListItem.Fields.ContainsField("LeaveApplicationID"))
{
// do leave application stuff
}
else
if (properties.ListItem.Fields.ContainsField("ExpenseFormID"))
{
// do expense form stuff
}

Symptoms:
- The code
properties.ListItem.Fields.ContainsField("ExpenseFormID")
did not pick up the field. It would return false for the content type "Expense Form" even when one was selected.
- This error was not consistent, some of the time it would work. It appears that in the background the content type information was still being updated whilst the ItemUpdated() event was fired.
- When a debugging break was put on the listItem.ContentType MOSS would return the default content type

Workarounds:

Workaround ItemUpdated() event:
Credit to MKeeper this was his solution. Thank you.
I put a counter in code as well just in case the item never checks in.

int counter = 0;
while( (item.File.CheckOutStatus != SPCheckOutStatus.None) && (counter != 6) )
{
// sleep for 2 seconds and try again
System.Threading.Thread.Sleep(2000);
counter++;
}

Workaround ItemUpdating() event:
Dont use the properties.ListItem.Fields.ContainsField("ExpenseFormID") method in the API. The lookup field that I use "ExpenseFormID" in the edit form was unique to the "Expense Form" content type. "ExpenseFormID" did not exist in any of the other content type it was ONLY in the "Expense Form" content type.
ItemUpdating() works well as it is synchronous so the items are updated by the time the user gets back to the list view after having hit the "OK" or "Check In" button on the edit form.

Check on the existance of "ExpenseFormID" in the AfterProperties object (see code snippet).

if (properties.ListItem.AfterProperties["ExpenseFormID"] != null)
{
// it is safe to use "ExpenseFormID" and the content type is "Expense Form"
}
else
{
// not "Expense Form" dont use "ExpenseFormID"
}


4 comments:

VadimK said...

Read your post and thought you might know how to 'intercept' moss page event and prevent moss from stripping/adding html in article content i.e. html comments. If you can point me in general direction of the solution to this problem, I would really appriciate it.

Garth said...

VadimK,
I'm not sure exactly what you are trying to do here. At what point do you want to intercept the page on the "Edit Form" of a list item?
Editing the Existing Edit Forms of SharePoint require you to use SharePoint Designer. You cannot use code in SharePoint designer.

With regards to MOSS Pages they are ASP.Net pages. So if you create your own page using Visual Studio and put it in the 12 hive it will behave as a ASP.Net page so you can use Page_Load or the normal ASP.Net page events.

Biju said...

Hi,



I'm overriding the ItemUpdating event; here I have a field "Start Date”. Now when a user edits the Start date of an item and clicks "OK", I need to capture the previous and latest start date and compare them.



I'm able to getthe previous date by the following code



SPWeb contextWeb = properties.OpenWeb();

SPList spList = contextWeb.Lists[properties.ListId];

SPListItem contextItem = spList.GetItemById(properties.ListItemId);





current_id = Convert.ToInt32(contextItem["ID"]);



if (contextItem["Start Date"] != null)

current_StartDate = (DateTime)contextItem["Start Date"];





But am not able to get the latest(committed) Start date.



I tried using the AfterProperty, but no luck :(





if (properties.AfterProperties["Start Date"] != null)

updated_StartDate = Convert.ToDateTime(properties.AfterProperties["Start Date"].ToString());







Can you help me in finding the solution for the same





PS: I have posted my query on Microsoft’s site also http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/fc56a0f9-9688-479f-b984-5ee3809e72cb/#7259f555-3803-4220-9357-beb4397452d8

Storminator16 said...

I'm sure by now you found the answer to your question, Biju:

http://blogs.msdn.com/varun_malhotra/archive/2009/05/25/modify-a-date-time-value-field-in-event-handlers-through-afterproperties.aspx

That was a great bit of pain for me over the course of a day.