0

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:

  1. Check if both dates exist, if not, show error and don't update the item.
  2. Check if EndDate is later than or equal to StartDate, if not, show error and don't update the item.
  3. 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:

  1. A user edits an item without proper dates, changes its Status to "Ending" and hits "Save".
  2. ValidateForm returns false, the custom error message appears.
  3. A user edits that item again, provides correct dates, changes Status to "Ending" and hits "Save".
  4. 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:

  1. 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.
  2. When a user just after that edits the item again to provide correct dates, OnItemUpdating in another instance of SPItemEventReceiver starts.
  3. 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 first OnItemUpdating 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!

  • Can you post the code for StartSomething, it's possible to update other lists from an event receiver so it sounds like it's either start StartSomething or some other code that is the real culprit here. – Gilgamesh Jan 15 '16 at 17:00
1

The problem turned out to be inside StartSomething: it was trying to update the very same item, upon which the OnItemUpdating had been fired.

To solve a problem it is sometimes enough to write it down :)

0

What you have:

  • You need to use an ..ing event to be able to cancel event
  • From the ..ing event, I suspect you do edit the current item. This update happens right before the actual update, hence the save conflict.

Some notes:

I then would go for the following approach:

  • Check your conditions in ..ing event. Cancel update if you want.
  • Save with this technique the state you want to persist.
  • Retrieve that state flag from the ..ed event and do all your stuff there if needed.

Or:

  • Check your conditions in ..ing event. Cancel update if you want.
  • Check again the same conditions in ..ing event (thanks to the BeforeProperties) and do what you want if needed.
  • "you do edit the current item" - yes, that turned out to be the case. Thank you! – Sergei Tikhomirov Jan 15 '16 at 17:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.