Thursday, October 23, 2008

Elevation nightmares

Recently I've been struggling with an all too common error in Sharepoint development:

"The operation is not valid due to the current state of the object"

Elevation of code is necessary in Sharepoint coding because, depending on the user in context at the time the application code executes, an un-elevated version of the code may or may not work. You need to have the correct rights as the user running the application code at a given point in time in order to have it execute successfully.

Up until now I'd been doing all my elevation like so:


SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite elevatedSite = new SPSite(siteUrl))
        {
            using (SPWeb elevatedWeb = elevatedSite.OpenWeb())
            {
                SPList elevatedList = elevatedWeb.Lists["Tasks"];
                if (elevatedList != null)
                {
                    SPListItem elevatedListItem = elevatedList.Items[0];
 
                    Console.WriteLine("The task is: " + elevatedListItem["Task"].ToString());
                    Console.WriteLine("The due date for the task is: " + elevatedListItem["DueDate"].ToString());
                    Console.WriteLine("The task was created by: " + elevatedListItem["Creator"].ToString());
                }
            }
        }                
    });



This code block will work flawlessly - the details of the task output to the console.

But the problem comes when I try to update the list item...


SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite elevatedSite = new SPSite(siteUrl))
        {
            using (SPWeb elevatedWeb = elevatedSite.OpenWeb())
            {
                SPList elevatedList = elevatedWeb.Lists["Tasks"];
                if (elevatedList != null)
                {
                    SPListItem elevatedListItem = elevatedList.Items[0];
 
                    Console.WriteLine("The task is: " + elevatedListItem["Task"].ToString());
                    Console.WriteLine("The due date for the task is: " + elevatedListItem["DueDate"].ToString());
                    Console.WriteLine("The task was created by: " + elevatedListItem["Creator"].ToString());
 
 
                    try
                    {
                        elevatedListItem["DueDate"] = DateTime.Now.AddDays(12).ToString();
                        try
                        {
                            elevatedWeb.AllowUnsafeUpdates = true;
                            elevatedListItem.Update();
                        }
                        catch (SPException updateEx)
                        {
                            Console.WriteLine("There was an error updating the list item. " + updateEx.ToString());
                        }
                        finally
                        {
                            elevatedWeb.AllowUnsafeUpdates = false;
                        }
                    }
                    catch (SPException updateFieldValueEx)
                    {
                        Console.WriteLine("Error updating field value for column 'DueDate'. " + updateFieldValueEx.ToString());
                    }
                }
            }
        }                
    });


The error will occur:
"The operation is not valid due to the current state of the object"

Basically, in short, there appears to be a bit of an issue with updating an item inside an elevated wrapper. My best guess as to the reason this is occurring is that it is due to the fact that elevation of code will not allow for updates to occur because then the update would be occuring as an elevated user and would not contain information on the user in context running the code which is of course used to update the modified by value on a list item, though I have not found any official line to support this theory.

The Solution

The solution is to rejig your code slightly and do your update outside of your elevation wrapper, like so:


// Declare list item, list and web variables outside of elevation block
 
SPListItem elevatedListItem = null;
SPList elevatedList = null;
SPWeb elevatedWeb = null;
 
// Elevate to get hold of the list item
 
SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite elevatedSite = new SPSite(siteUrl))
        {
            elevatedWeb = elevatedSite.OpenWeb();
 
            elevatedList = elevatedWeb.Lists["Tasks"];
            if (elevatedList != null)
            {
                elevatedListItem = elevatedList.Items[0];
            }
 
        }                
    });
 
// Do the output and update
 
Console.WriteLine("The task is: " + elevatedListItem["Task"].ToString());
Console.WriteLine("The due date for the task is: " + elevatedListItem["DueDate"].ToString());
Console.WriteLine("The task was created by: " + elevatedListItem["Creator"].ToString());
 
if (elevatedListItem != null)
{
 
    try
    {
        elevatedListItem["DueDate"] = DateTime.Now.AddDays(12).ToString();
        try
        {
            elevatedWeb.AllowUnsafeUpdates = true;
            elevatedListItem.Update();
        }
        catch (SPException updateEx)
        {
            Console.WriteLine("There was an error updating the list item. " + updateEx.ToString());
        }
        finally
        {
            elevatedWeb.AllowUnsafeUpdates = false;                                           
        }
    }
    catch (SPException updateFieldValueEx)
    {
        Console.WriteLine("Error updating field value for column 'DueDate'. " + updateFieldValueEx.ToString());
    }
}


That should do it! There should be no more operation is invalid error.

Fixes to common .NET problems, as well as information on .NET features and solutions to common problems that are not language-specific.

Fixes to common .NET problems, as well as information on .NET features and solutions to common problems that are not language-specific.

Z