I'm developing an application with Sharepoint 2010. I've got a custom list. Every item of the list has, among others, the following fields:
- StartDate
- EndDate
- Status (enum with values like "In progress", "Ending", etc)
If a user changes the value of Status to "Ending", the following must occur:
- Check if both dates exist, if not, show error and don't update the item.
- Check if EndDate is later than or equal to StartDate, if not, show error and don't update the item.
- If both dates exist and are correct, update the item and launch some process (let's call it
StartSomething
).StartSomething
does multiple things, e.g.: create new item in another list, edit an item in yet another list, send a bunch of emails.
I've got the following (slightly edited) code to implement my task:
public class OnEnding : SPItemEventReceiver {
private const string DatesErrorMessage = "Please provide correct dates"
public override void ItemUpdating(SPItemEventProperties properties)
{
base.ItemUpdating(properties);
if (IntentEnding(properties))
if (ValidateForm(properties))
StartSomething(properties);
}
/* Return true, iff Status is being changed to "Ending" */
private static bool IntentEnding(SPItemEventProperties properties)
{
var beforeValue = properties.ListItem[StatusField];
var afterValue = properties.AfterProperties(StatusFieldInternalName);
return beforeValue != afterValue && afterValue == "Ending";
}
/* Return true, iff StartDate <= EndDate and both dates exist and are correctly formatted */
private static bool ValidateForm(SPItemEventProperties properties)
{
DateTime startDate;
DateTime endDate;
if ((DateTime.TryParse(properties.AfterProperties(StartDateFieldInternalName), out startDate) == false) ||
(DateTime.TryParse(properties.AfterProperties(EndDateFieldInternalName), out endDate) == false))
{
properties.Status = SPEventReceiverStatus.CancelWithError;
properties.ErrorMessage = DatesErrorMessage;
return false;
}
if (startDate > endDate)
{
properties.Status = SPEventReceiverStatus.CancelWithError;
properties.ErrorMessage = DatesErrorMessage;
return false;
}
return true;
}
}
I observe the following behavior:
- A user edits an item without proper dates, changes its Status to "Ending" and hits "Save".
ValidateForm
returnsfalse
, the custom error message appears.- A user edits that item again, provides correct dates, changes Status to "Ending" and hits "Save".
- A new error appears, BUT
StartSomething
launches anyway! The error:
Save Conflict
Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.
At first I thought of moving StartSomething
to OnItemUpdated
, but this is not an option: StartSomething
is to be launched not after every edit, but only if the Status had been changed to "Ending", but after OnItemUpdating
exits, OnItemUpdated
has no idea if that was the case.
Then I thought of utilizing Sharepoint's list validation feature, but as it turned out, it doesn't prevent "Save conflict" error. I assume that OnItemUpdating
launches even when the list validation formula returns false
.
My assumption is the following:
- When a user tries to save an item with "Ending" status but without proper dates,
OnItemUpdating
starts but doesn't exit properly due the custom error caused by validation failure. - When a user just after that edits the item again to provide correct dates,
OnItemUpdating
in another instance ofSPItemEventReceiver
starts. - When a user hits "Save",
OnItemUpdated
must be called in order to finally save the changes, but it doesn't know which ones: from the firstOnItemUpdating
or from the second one.
UPD More testing showed that what causes problems is probably that StartSomething
(being called inside OnItemUpdating
) updates items in other lists via item.Update()
. After commenting out updating items the bug goes away. But the question remains: how do I properly update items in other lists inside this list's OnItemUpdating
?
Any help would be greatly appreciated!
StartSomething
, it's possible to update other lists from an event receiver so it sounds like it's either startStartSomething
or some other code that is the real culprit here.