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
Thanks for blogging a workaround to this strange issue.
ReplyDeleteWithout your blog I would have smashed my computer to bits in utter frustration-lol!
Excellent, nice to hear you got it sorted out :) Keep on MOSSing.
ReplyDeleteThanks man. I was having this exact same problem, but with WSS. Saved me wasting another afternoon.
ReplyDeleteI am trying to execute this below code
ReplyDeleteusing (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
Hi,
ReplyDeleteit 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.
I modified the code as following
ReplyDeleteusing (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
I have a custom column "Category" in my Discussion list.
ReplyDeletehow 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
Hi,
ReplyDeleteif 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.
Thank you for this great post. Your code solves a lot of my problem.
ReplyDeletenice work. my only suggestion would be to dispose of the web at the end of the code block. i.e. web.Dispose();
ReplyDeleteAbsolutely! 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 :)
ReplyDeleteBut the question is, should I also dispose the SPWeb web that I pass in as a parameter?
Hey Jussi.......Thanks a ton for the solution, i almost wasted a day in figuring it out.
ReplyDeleteThe Post was really very information.
ReplyDeleteI 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.
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.
ReplyDeleteThanks a lot , your post helps me !
ReplyDeleteI've been spending 5 hrs with this issue
I dont have to smash my head on the table no more
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.
ReplyDeleteGreat solution!!!
ReplyDeletewow. 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.
ReplyDeleteyew da man.....
The magic of the Internet and Jussi.
ReplyDeleteThanks.
Excellent!!
ReplyDeleteThank you for this great workarround. It works!
Hello There,
ReplyDeleteI'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
It clearly shouldn't work this way, but it does. Fantastic help, many thanks
ReplyDeleteThanks for saving my time and it was really great work.
ReplyDeleteThank you very much..
ReplyDeleteIt worked for me !!
You saved my day !!
Actually there is a much easier solution:
ReplyDeleteIt 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
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!!!
ReplyDeleteThanks for posting this. It saved a lot of time..
ReplyDelete-Vani
Try this method of security elevation. (Andrew Maisey)
ReplyDeleteSPSite 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;
}
}
Great approach to the issue and very simple to implement. Thanks a lot.
ReplyDeleteThanks for providing the solution.
ReplyDeleteGreat.I was struggling with this for 2 hours.
This is not a work around. Instead this is the way you should write a code.
ReplyDeleteWhen 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{}
});
@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@Jussi
ReplyDeleteIt would work inside SPSecurity.RunWithElevatedPriveleges
if you app-pool account has the permission to do that.
Thanks to Stefan.
ReplyDeleteYou saved my day.
Thanks for a post. It was helpful :)
ReplyDeleteThis 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.
ReplyDeleteThanks ! Simple workaround.
ReplyDeleteThough 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
The exact reason that the SPWEB object created outside the delegate
ReplyDeleteread 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
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsecurity.runwithelevatedprivileges.aspx
ReplyDeletethe exact reason the SPWEB in Jussi code created outside the delegate.
ReplyDeleteRead 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.