This is to summarize the inconsistent behaviors I encountered when working with "AfterProperties" in "ItemUpdating" event handler on a custom SharePoint list in SP 2010.
1) AfterProperties will be NULL when ItemUpdating event is called because of a code-update to list:
SPFieldLookupValue oldLookupValue = new SPFieldLookupValue(properties.ListItem["LookupColumn"].ToString());
SPFieldLookupValue newLookupValue = new SPFieldLookupValue(properties.AfterProperties["LookupColumn"].ToString());
if(oldLookupValue != newLookupValue)
{
//look up column value has changed
}
int oldLookupID = 0;
int newLookupID = 0;
if (!string.IsNullOrEmpty(oldLookupValue))//Get the old Lookup id
{
SPFieldLookupValue oldLookup = new SPFieldLookupValue(oldLookupValue); oldLookupID = oldLookup.LookupId;
1) AfterProperties will be NULL when ItemUpdating event is called because of a code-update to list:
This is something that is not documented, and lead me into a lot of trouble:
Consider this scenario:
My SharePoint list had a column named "Email".
I wrote an "item-updating" event handler that will update a few other child lists with updated Email, when it was altered in the parent list..(to which the event handler was attached)
Here is the code:
string oldEmail = (properties.ListItem["Email"] == null) ? "" : properties.ListItem["Email"].ToString();
string newEmail = (properties.AfterProperties["Email"] == null) ? "" : properties.AfterProperties["Email"].ToString();
if(oldEmail != newEmail )
{
//Update the other child lists with "newEmail"
}
I tested this code by editing the list item and updating the Email and the change was correctly propagated to the other lists.
After a few days we noticed that a lot of items in those child lists, had empty email values.
Reason:
When the item in parent list is edited directly using the SharePoint interface, then both properties.ListItem["Email"] and properties.AfterProperties["Email"] has the expected values.
But, when the list is being updated by code, and the code does not update the "Email" (for example, the code just updates "First Name" in the list) then properties.AfterProperties["Email"] will be NULL in the event receiver code. AfterProperties will have the correct value, only when Email is also updated through code.
This was not the case when editing was done through SharePoint UI.( i.e If I edit that item and change only the "First Name", then AfterProperties["Email"] had the correct value..)
Because of this my event receiver had reset Email in all the child lists with empty values.
Resolution:
Unfortunately, the only resolution available was to check whether "AfterProperties" is NULL and in that case assume that the list item is updated through code somewhere else, and do nothing..
2) Dealing with "Lookup" columns:
If you are handling a "Lookup" column using "AfterProperties", then be careful. It looks like a bug where "AfterProperties" contains only the "ID" part of look-up and not the whole look-up value.
Consider this example, where I am checking whether a look up column value has been altered:
SPFieldLookupValue newLookupValue = new SPFieldLookupValue(properties.AfterProperties["LookupColumn"].ToString());
if(oldLookupValue != newLookupValue)
{
//look up column value has changed
}
The above code looks correct, but simply would not work in "ItemUpdating" event.
Reason:
Value of properties.ListItem["LookupColumn"].ToString() will have the correct look up value like "3;#karthik", but the value of properties.AfterProperties["LookupColumn"].ToString() will have only "3" (just the look up ID).
Resolution:
Here is the workaround that I implemented to get around this issue:
string oldLookupValue = (properties.ListItem["LookupColumn"] == null) ? "" : properties.ListItem["LookupColumn"].ToString();
string newLookupValue = (properties.AfterProperties["LookupColumn"] == null) ? "" : properties.AfterProperties["LookupColumn"].ToString();
int oldLookupID = 0;
int newLookupID = 0;
if (!string.IsNullOrEmpty(oldLookupValue))//Get the old Lookup id
{
SPFieldLookupValue oldLookup = new SPFieldLookupValue(oldLookupValue); oldLookupID = oldLookup.LookupId;
}
if (!string.IsNullOrEmpty(newLookupValue))//Get the new Lookup id
{
newLookupID = Convert.ToInt32(newLookupValue);
}
if (!string.IsNullOrEmpty(newLookupValue))//Get the new Lookup id
{
newLookupID = Convert.ToInt32(newLookupValue);
}
if (oldLookupID != newLookupID)//Lookup has been updated
{ //value has changed
{ //value has changed
}
Another solution would be to use string parsing methods to compare just the ID part.
Very very useful post, solved my issue.
ReplyDeleteThanks,
Shreecanth
very useful article solved my problem.
ReplyDeleteThanks,
Shreecanth
Thanks for the comment..Glad to know it helped.
ReplyDeleteThanks for posting this, saved me a lot of effort and downtime that I would have needed to figure out what was happening!
ReplyDeleteThank you very much, your post saved me a lot of time!
ReplyDeleteme too!
DeleteI just encountered this problem after 5 years programming for SharePoint... shouldn't this be considered a bug?
ReplyDeleteThis is what I tried as per your given code, my new value is getting null and also an exception at mentioned statement
ReplyDeletestring oldLookupValue = (properties.ListItem["E-mail Address"] == null) ? "" : properties.ListItem["E-mail Address"].ToString();
string newLookupValue = (properties.AfterProperties["E-mail Address"] == null) ? "" : properties.AfterProperties["E-mail Address"].ToString();
int oldLookupID = 0;
int newLookupID = 0;
if (!string.IsNullOrEmpty(oldLookupValue))//Get the old Lookup id
{
SPFieldLookupValue oldLookup = new SPFieldLookupValue(oldLookupValue); oldLookupID = oldLookup.LookupId;
}
if (!string.IsNullOrEmpty(newLookupValue))//Get the new Lookup id
{
newLookupID = Convert.ToInt32(newLookupValue); //Null exception
}
if (oldLookupID != newLookupID)//Lookup has been updated
{ //value has changed
properties.Cancel = true;
}
So just to give some extra test results..
ReplyDeleteI was curious what happened with the UI and nulls or blanks.
I created an SPList called testList with the additional:
single line text field
datetime field
number field
user field
lookup field
then associated a list event for itemUpdating to see what actually
would get submitted from the UI and how it would act on
subsequent posts.
public override void ItemUpdating(SPItemEventProperties properties)
{
object testString1Before = properties.ListItem["testString1"];
object testString1After = properties.AfterProperties["testString1"];
object testNumber1Before = properties.ListItem["testNumber1"];
object testNumber1After = properties.AfterProperties["testNumber1"];
object testDate1Before = properties.ListItem["testDate1"];
object testDate1After = properties.AfterProperties["testDate1"];
object testUser1Before = properties.ListItem["testUser1"];
object testUser1After = properties.AfterProperties["testUser1"];
object testLookup1Before = properties.ListItem["testLookup1"];
object testLookup1After = properties.AfterProperties["testLookup1"];
}
The case was always that:
properties.ListItem was always null.
AfterProperties were always "". I guess empty string.
So I guess that your assumtion that if an afterproperty is
null then it is not a UI update is correct.
Even if I filled in a value for testString1, updated the
list item, went back to the UI edit and cleared the text box,
the afterproperty of "" would set the list item field value to
null. So the effect of setting an SPListItem to "" I guess
essentially means set the value to null by internal sharepoint
processes. could should test from code to see if
listitem["testString1"] = "";
listitem.Update();
Actually sets it to null but am too lazy to test it. : )
OK so I got un-lazy..
Deletefrom custom code setting a listitem form field equal to either "" or null will show up in the afterproperties as "".
example:
SPList list1 = web.Lists["testList"];
SPListItem itm = list1.GetItemById(1);
web.AllowUnsafeUpdates = true;
itm["testString1"] = null; //or using ""
itm.Update();
web.AllowUnsafeUpdates = false;
So that re-enforces that a null value in an Afterproperty means that it is not being used to make a change to the listItem.
thanks.. this was indeed a nice post..
ReplyDelete