SPWeb.ProcessBatchData throws “Value does not fall within the expected range” exception
Recently I needed to delete a large number of items from List and I tried to solve it by simply invoking Delete() method on each SPListItem object.
Function Clear-List([Microsoft.SharePoint.SPList]$list) {
$list.Items | ForEach-Object { $_.Delete() }
}
That did the trick but I wasn’t really satisfied with the results – the code was deleting an item every 2-3 seconds. That made me looking for alternative approache and eventually I came across http://merill.net/2008/02/efficiently-delete-purge-all-items-from-a-sharepoint-list/, it was looking like a solution to my problem, so I took the idea and built my own function using XmlTextWriter instead of StringBuilder.
When I tried to run it, I was surprised – it didn’t work. The ProcessBatchData method was throwing Value does not fall within the expected range exception for no apparent reason, so I switched back to StringBuilder method from original article and that surprised me again – it worked.
That intrigued me enough to start annihilating any difference between generated XML strings by both methods. I was shocked when during my very last iteration, I discovered the case of encoding declaration to be cause for this exception. Take a look by yourself:
A non working document declaration produced by XmlTextWriter.WriteStartDocument() method:
<?xml version="1.0" encoding="utf-8"?>
A working document declaration produced by code from article:
<?xml version="1.0" encoding="UTF-8"?>
Looks like a bug with ProcessBatchData method – maybe someday I will open up a case with Microsoft but for now as a workaround we can simply either fallback to StringBuilder method or replace utf-8 with UTF-8 before invoking ProcessBatchData method.
Function Clear-List([Microsoft.SharePoint.SPList]$list) {
$requestStream = New-Object System.IO.MemoryStream
$requestWriter = New-Object System.Xml.XmlTextWriter($requestStream, [System.Text.Encoding]::UTF8)
$requestWriter.WriteStartDocument()
$requestWriter.WriteStartElement("Batch")
$list.Items | ForEach-Object {
$requestWriter.WriteStartElement("Method")
$requestWriter.WriteStartElement("SetList")
$requestWriter.WriteAttributeString("Scope", "Request")
$requestWriter.WriteString($list.ID)
$requestWriter.WriteEndElement()
$requestWriter.WriteStartElement("SetVar")
$requestWriter.WriteAttributeString("Name", "ID")
$requestWriter.WriteString($_.ID)
$requestWriter.WriteEndElement()
if($list.BaseType -eq [Microsoft.SharePoint.SPBaseType]::DocumentLibrary) {
$requestWriter.WriteStartElement("SetVar")
$requestWriter.WriteAttributeString("Name", "owsfileref")
$requestWriter.WriteString($_.File.ServerRelativeUrl)
$requestWriter.WriteEndElement()
}
$requestWriter.WriteStartElement("SetVar")
$requestWriter.WriteAttributeString("Cmd", "Delete")
$requestWriter.WriteEndElement()
$requestWriter.WriteEndElement()
}
$requestWriter.WriteEndElement()
$requestWriter.WriteEndDocument()
$requestWriter.Flush()
$requestStream.Seek(0, [System.IO.SeekOrigin]::Begin)
$requestReader = New-Object System.IO.StreamReader($requestStream)
$requestText = $requestReader.ReadToEnd()
$requestStream.Close()
$requestText = $requestText.Replace("utf-8", "UTF-8")
$result = $list.ParentWeb.ProcessBatchData($requestText)
}