June 27, 2007

MOSS: SPListItem.Update() throws error Operation is not valid due to the current state of the object.

Problem:
When running SPListItem.Update commands inside SPSecurity.RunWithElevatedPrivileges block you get error: Operation is not valid due to the current state of the object.

Thoughts:
I will call this a workaround since I don't know why it works and is this really how it should be done.

Workaround:
You must not call the SPListItem.Update inside the RunWithElevatedPrivileges block. Instead you should only instantiate the SPSite or SPWeb there and call Update afterwards, like this:

    private static string CreateSiteLink(SPWeb newSite, int groupId, string text)
    {
        string retVal = "";
        SPWeb elevatedRootWeb = null;

try
{
         SPSecurity.RunWithElevatedPrivileges(delegate()
         {
             using (SPSite elevatedSite = new SPSite(newSite.Site.ID))
             {
                 elevatedRootWeb = elevatedSite.RootWeb;
             }
         });
 
         SPList userInformation = elevatedRootWeb.Lists["User Information List"];
  
         if (userInformation != null)
         {
             try
             {
                 elevatedRootWeb.AllowUnsafeUpdates = true;
  
                 SPListItem item = userInformation.GetItemById(groupId);
  
                 if (item["Notes"] != null)
                 {
                     item["Notes"] = string.Format(text, newSite.ServerRelativeUrl, newSite.Name);
                     item.Update();
                 }
  
                 elevatedRootWeb.AllowUnsafeUpdates = false;
             }
             catch (Exception e)
             {
                 retVal = " Site link for group " + groupId + " couldn't be created. (" + e.Message + ")";
             }
         }
         else
         {
             retVal = " Site link for group " + groupId + " couldn't be created. (User Information List not found)";
         }
finally
{
elevatedRootWeb.Dispose();
}
  
        return retVal;
    }


Solution:
Unknown

40 comments:

  1. Thanks for blogging a workaround to this strange issue.

    Without your blog I would have smashed my computer to bits in utter frustration-lol!

    ReplyDelete
  2. Excellent, nice to hear you got it sorted out :) Keep on MOSSing.

    ReplyDelete
  3. Thanks man. I was having this exact same problem, but with WSS. Saved me wasting another afternoon.

    ReplyDelete
  4. I am trying to execute this below code
    using (SPWeb site = new SPSite("http://ibnjdevdt:100/sites/DemoCaseTracker").OpenWeb())
    {
    site.AllowUnsafeUpdates = true;
    SPListItem discussion = SPUtility.CreateNewDiscussion(list.Items, subject);
    discussion[SPBuiltInFieldId.Body] = body;
    discussion.Update();

    // this is only to ensure the Xml can be generated
    object t = discussion[SPBuiltInFieldId.ThreadIndex];

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(discussion.Xml);
    return doc.DocumentElement;
    }
    but ending up with exception message
    Microsoft.SharePoint.SPException was unhandled by user code
    Message="The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again."
    Source="Microsoft.SharePoint"
    ErrorCode=-2130575251
    any suggestions where ia m going wrong!
    Advance Thanks

    ReplyDelete
  5. Hi,
    it doesn't show how you create the "list" object, so the problem might be related to that.

    Also using SPUtility might mean that you should also set

    site.Site.AllowUnsafeUpdates = true;

    Can you try that and see what happens. Also I'd need to know on what line does the exception gets raised from.

    ReplyDelete
  6. I modified the code as following

    using (SPWeb site = new SPSite("http://ibnjdevdt:100/sites/DemoCaseTracker").OpenWeb())
    {
    SPList list = site.GetList("http://ibnjdevdt:100/sites/DemoCaseTracker/Lists/CaseDiscussions");// FindList(listName);
    if (list == null)
    throw new ArgumentException("List name is invalid.");

    site.AllowUnsafeUpdates = true;
    SPListItem discussion = SPUtility.CreateNewDiscussion(list.Items, subject);
    discussion[SPBuiltInFieldId.Body] = body;
    discussion.Update();

    // this is only to ensure the Xml can be generated
    object t = discussion[SPBuiltInFieldId.ThreadIndex];

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(discussion.Xml);
    return doc.DocumentElement;
    }
    according to your point regarding list defiantion and now its working fine
    Thanks a lot Jussi

    ReplyDelete
  7. I have a custom column "Category" in my Discussion list.
    how can I update it using "SPBuiltInFieldId"?
    I tried with SPBuiltInFieldId.Category i.e.
    discussion[SPBuiltInFieldId.Category] = Category;
    but ending up with exception
    "Invalid field name. {6df9bd52-550e-4a30-bc31-a4366832a87d}"
    any suggestion for accessing custom column with SPBuiltInFieldId?thanks in advance

    ReplyDelete
  8. Hi,
    if you would like to really modify properties of the field, you should do it like this:

    newsList.Fields["Category"].DefaultValue = "some default value";

    But if you would like to modify the value of that field on some specific list item, you do it like this:

    newsList.GetItemById(0)["Category"] = "some value";

    If it really is a custom field and not builtin MOSS field, I believe you should be getting that error since the GUID of SPBuiltInFieldId.Category refers to MOSS's builtin Category field and not your custom Category field.

    ReplyDelete
  9. Thank you for this great post. Your code solves a lot of my problem.

    ReplyDelete
  10. nice work. my only suggestion would be to dispose of the web at the end of the code block. i.e. web.Dispose();

    ReplyDelete
  11. Absolutely! Thanks for pointing that out. Believe or not, I already had that disposal in the original source code. I just hadn't included it in this blog post :)

    But the question is, should I also dispose the SPWeb web that I pass in as a parameter?

    ReplyDelete
  12. Hey Jussi.......Thanks a ton for the solution, i almost wasted a day in figuring it out.

    ReplyDelete
  13. The Post was really very information.
    I have a situation where in While a site is getting created,a feature is activated. This feature creates document content types on the Document Libraties in the new created site.

    All worked Fine on the Stageing server. But this does not work fine on the Production Server.

    Upon Debugging I get an error
    "The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.
    ErrorCode -2130575251 int "

    This again only happens when many users hit to create site simultaneously.

    Can you please give me some insite in this issue.

    ReplyDelete
  14. Thanks for the information. Spent two days trying to work this out. I was using workflow as an example but it seems workflow has a different set of rules on how to handle Item updates.

    ReplyDelete
  15. Thanks a lot , your post helps me !
    I've been spending 5 hrs with this issue

    I dont have to smash my head on the table no more

    ReplyDelete
  16. You rock man..Spent one full day to figure out why the list Title was not getting updated. List.Update somehow was not working inside the "RunWithElevatedPrevilages" and your code solved my problem.

    ReplyDelete
  17. wow. this is in absolutely none of the books and when i first saw it i thought it wouldnt work. except it does and it fixed my problem.

    yew da man.....

    ReplyDelete
  18. The magic of the Internet and Jussi.

    Thanks.

    ReplyDelete
  19. Excellent!!
    Thank you for this great workarround. It works!

    ReplyDelete
  20. Hello There,

    I'm in need of help. I'm getting the same error when I want to deploy the sharepoint project! and I can't have access to my SharePoint site!
    Can you help me in this regard? what should I do??

    please email me at s0lmzz@yahoo.com

    ReplyDelete
  21. It clearly shouldn't work this way, but it does. Fantastic help, many thanks

    ReplyDelete
  22. Thanks for saving my time and it was really great work.

    ReplyDelete
  23. Thank you very much..

    It worked for me !!

    You saved my day !!

    ReplyDelete
  24. Actually there is a much easier solution:

    It seems to me that RunWithElevatedPrivileges() has a bug such that it does NOT use the Context of the new user.
    You can force it to do so by simply setting the context to null, like here:

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    HttpContext.Current = null;
    using (SPSite site = new SPSite(targetWeb))
    {
    using (SPWeb web = site.OpenWeb())
    {
    SPList targetlist = web.Lists[targetList];
    foreach (ItemType item in import.item)
    {
    ....
    }
    }
    }
    });

    best regards,
    Stefan Mader

    ReplyDelete
  25. THANKS. My Problem was, that the Code was running from an application page, but not from an own webservice. Your workaround is working for both. THANKS!!!

    ReplyDelete
  26. Thanks for posting this. It saved a lot of time..
    -Vani

    ReplyDelete
  27. Try this method of security elevation. (Andrew Maisey)

    SPSite objInitialSite = new SPSite("http://site/");
    SPUser objUser = objInitialSite.SystemAccount;

    objInitialSite.Dispose();
    //SPSecurity.RunWithElevatedPrivileges(delegate
    //{
    using (SPSite site = new SPSite("http://site/", objUser.UserToken))
    {
    using (SPWeb web = site.OpenWeb())
    {
    web.AllowUnsafeUpdates = true;
    SPList list = web.Lists["Suppliers"];
    SPListItem newItem = list.Items.Add();

    newItem["Supplier Name"] = txtSupplierName.Text;
    newItem["Category"] = cboCategory.SelectedItem.ToString();
    newItem["SAP Vendor Number"] = txtSAPVendorNo.Text;
    newItem["SAP Man Vendor Number"] = txtSAPManVendorNo.Text;
    newItem["Website"] = txtWebsite.Text;
    newItem["Notes"] = string.Empty;
    newItem["Link"] = "http://is/";
    newItem["Buyer"] = cboBuyer.SelectedItem.ToString();

    newItem.Update();
    web.AllowUnsafeUpdates = false;
    }
    }

    ReplyDelete
  28. Great approach to the issue and very simple to implement. Thanks a lot.

    ReplyDelete
  29. Thanks for providing the solution.

    Great.I was struggling with this for 2 hours.

    ReplyDelete
  30. This is not a work around. Instead this is the way you should write a code.

    When you are instantiating SPWeb object it opens the web with the current credentials.

    So, when you call SPListItem.Update() method it uses the privileges of that user, even if you are writing code inside SPSecurity.RunWithElevatedPriveleges()

    So, correct method is when you use
    SPSecurity.RunWithElevatedPriveleges() you should instantiate SPWeb object inside the delegate of SPSecurity.RunWithElevatedPriveleges(). For ex:

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    try
    {
    using(SPSite site = new SPSite(SPContext.Current.Site.Id)){
    using(SPWeb web = site.OpenWeb()){
    //your code here.
    }
    }
    }
    catch{
    }
    finally{}
    });

    ReplyDelete
  31. @Nikhil: Yes, that is how it should be done, but it just won't work like that if you use SPListItem.Update inside SPSecurity.RunWithElevatedPrivileges.

    ReplyDelete
  32. @Jussi
    It would work inside SPSecurity.RunWithElevatedPriveleges

    if you app-pool account has the permission to do that.

    ReplyDelete
  33. Thanks to Stefan.
    You saved my day.

    ReplyDelete
  34. Thanks for a post. It was helpful :)

    ReplyDelete
  35. This is a bug in SharePoint which is corrected in SharePoint 2010. One work-around is to get SPContext.Current before elevating privileges. I don't like the way you do it in your post. You work around it but do not know why or how it works. Also, your code becomes harder to read. Try using reflector on SPControl.SPWebEnsureSPControl and SPListItem.AddOrUpdateItem and you may understand better what is happening.

    ReplyDelete
  36. Thanks ! Simple workaround.
    Though I do not understandthe exact reason. And I guess whenwe call SPContext.Current before this, we do not face any issues. If someone can let me know why we have such problem it would be great.

    Thanks

    ReplyDelete
  37. The exact reason that the SPWEB object created outside the delegate

    read this from Microsoft :

    An SPSite object created outside the delegate can be referenced inside the delegate, however, the methods and property assessors of the object run with the privileges of the user context in which the objects were created, not with the elevated privileges. The same point applies to SPWeb objects and any other objects. You must create new objects inside the delegate if you need to execute the members of the objects with elevated privileges. If the new object must represent the same persisted entity as an object created outside the delegate, then you must reference identification information from the externally created object and use it to create the new object within the delegate. For example, if web is a reference to an SPWeb object created before the call to RunWithElevatedPrivileges, then the following code shows you would use the ID of its parent SPSite object to construct a new SPSite object.

    http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsecurity.runwithelevatedprivileges.aspx

    ReplyDelete
  38. http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsecurity.runwithelevatedprivileges.aspx

    ReplyDelete
  39. the exact reason the SPWEB in Jussi code created outside the delegate.

    Read this :
    An SPSite object created outside the delegate can be referenced inside the delegate, however, the methods and property assessors of the object run with the privileges of the user context in which the objects were created, not with the elevated privileges. The same point applies to SPWeb objects and any other objects. You must create new objects inside the delegate if you need to execute the members of the objects with elevated privileges. If the new object must represent the same persisted entity as an object created outside the delegate, then you must reference identification information from the externally created object and use it to create the new object within the delegate. For example, if web is a reference to an SPWeb object created before the call to RunWithElevatedPrivileges, then the following code shows you would use the ID of its parent SPSite object to construct a new SPSite object.

    ReplyDelete