Wednesday 11 April 2012

Deleting Items on a List using SPWeb.ProcessBatchData()

I had a situation a week ago where I needed to write a script to delete all items in a list while maintaining the list structure. Now for those familiar with the SharePoint API, the obvious solution would be something like this:

foreach (SPListItem item in list.Items)
{
    item.Delete();
}


However theIEnumerator interface does not like deleting things while iterating through them because the list count will change while you're doing it. This is mighty confusing for an iterating loop so that's handled as an error and SharePoint sez, no can do, sorry. I tried using list.Items[0].Delete() as I thought that might work since there will always be at least one item in the collection, but no - when it got to the end of the collection it threw an error and all my old VB programming days of On Error Resume Next could not help me in my attempts to keep the code running after the error.


But anyway. All this has happily become a moot point since discovering the SPWeb.ProcessBatchData() command, since all I have to do is loop through every list in the web and issue a delete command. At first I got to the Microsoft help for this (always, always a bad idea. Sorry, Microsoft, but there it is) and got scared off by talk of OWS files and piles of XML. How and ever, after a bit of digging I found a great entry on Stack Overflow that assures me one does not need to be rooting around obscure XML files in the Twelve Hive in ungraceful fashion, but can build the XML on the fly as a string, feed it in to the ProcessBatchData command and hey presto, you're off:



foreach (SPList list in web.Lists)
   {
     try
       {
        //decrement loop so as not to confuse the iEnumerator interface
       Console.WriteLine("Deleting list items in following list : " + list.Title);
       SPListItemCollection splic = list.Items;
       StringBuilder batchString = new StringBuilder();
       batchString.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch>");
       foreach (SPListItem item in splic)
       {
         batchString.Append("<Method>");
         batchString.Append("<SetList Scope=\"Request\">" + Convert.ToString(item.ParentList.ID) + "</SetList>");
         batchString.Append("<SetVar Name=\"ID\">" + Convert.ToString(item.ID) + "</SetVar>");
         batchString.Append("<SetVar Name=\"Cmd\">Delete</SetVar>");
         batchString.Append("</Method>");
       }
       batchString.Append("</Batch>");
       web.ProcessBatchData(batchString.ToString());
    }
    catch { //whatever }

 }





Please note that I actually found this code somewhere on Stack Overflow, but have gone and lost the link. So Unknown Programmer, you have my greatest gratitude for this!

I believe there are Copy and Insert methods also but will have to check those out. I may have a requirement for these, due to SharePoint UI inflexibility, so stay tuned...

11 comments:

  1. Great post!
    Here's a very similar scrip I used to selectively clear SP 2007 lists: https://github.com/Aleksey-Kudryavtsev/BatchDelete

    ReplyDelete
  2. Thanks for the link and for commenting, Aleksey! welcome

    ReplyDelete
  3. Thank you to both yourself and Aleksey, that helped speed up my delete.

    ReplyDelete
  4. Hi Daniel! Thanks for commenting and you're welcome :)

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Hi susani !! Grt post... I tried the above code for a sample list containing 20 items with no luck. Not even change a single line from above

    static void Main(string[] args)
    {
    try
    {
    using (SPSite spSite = new SPSite("http://xyz"))
    {
    using (SPWeb spWeb = spSite.OpenWeb())
    {
    StringBuilder deletebuilder = BatchCommand(spWeb.Lists["Books"]);

    spWeb.ProcessBatchData(deletebuilder.ToString());
    }
    }

    }
    catch (Exception ex)
    {
    throw;
    }
    }
    private static StringBuilder BatchCommand(SPList spList)
    {
    //decrement loop so as not to confuse the iEnumerator interface
    Console.WriteLine("Deleting list items in following list : " + spList.Title);
    SPListItemCollection splic = spList.Items;
    StringBuilder batchString = new StringBuilder();
    batchString.Append("");
    foreach (SPListItem item in splic)
    {
    batchString.Append("");
    batchString.Append("" + Convert.ToString(item.ParentList.ID) + "");
    batchString.Append("" + Convert.ToString(item.ID) + "");
    batchString.Append("Delete");
    batchString.Append("");
    }
    batchString.Append("");
    return batchString;
    }

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. for some reasons values within APPEND()'s are not getting displayed above. But I have written them exactly like you have posted

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. HelloV Pats in sharepoint 2013 you need to use a different XML, passing also the file path to the owsfileref variable in the Method elements, as MSDN says.
    https://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.processbatchdata.aspx#Anchor_1
    here is an example.
    public static string DeleteAllItems(SPSite spSite, SPWeb spWeb, SPList spList)
    {
    StringBuilder deletebuilder = BatchCommand(spList);
    return spWeb.ProcessBatchData(deletebuilder.ToString());
    }

    private static StringBuilder BatchCommand(SPList spList)
    {
    StringBuilder deletebuilder = new StringBuilder();
    deletebuilder.Append("");
    string command = "" + spList.ID +
    "{0}{1}Delete";

    foreach (SPListItem item in spList.Items)
    {
    deletebuilder.Append(string.Format(command, item.ID.ToString(),item["FileRef"].ToString()));
    }
    deletebuilder.Append("");
    return deletebuilder;
    }

    ReplyDelete
  9. I am continually amazed by the amount of information available on this subject. What you presented was well researched and well worded in order to get your stand on this across to all your readers. Medium roast coffee

    ReplyDelete