Tuesday, April 20, 2010

Batch Updating SharePoint List Items

I have got a situation where I have to update a set of list items in a single transaction. I thought I would loop through the list item collection and do it, but I was hesitant to do this since I would degrade the server performance if there are more than 2000 items, as we all know looping through 2000 list items affects SharePoint server performance. Suddenly an idea came to my mind, thinking that if SQL update command updates multiple why shouldn't CAML does that. So started searching a Google search and here are some good articles which describes about Batch Updating SharePoint List Items.

http://msdn.microsoft.com/en-us/library/cc404818.aspx

http://dotnetstep.blogspot.com/2009/01/batch-update-in-sharepoint.html

http://blog.dynatrace.com/2009/01/20/sharepoint-using-batch-updates-to-speed-up-performance/

And here is my version of implementation…

// Set up the variables to be used.

StringBuilder methodBuilder = new StringBuilder();

string batch = string.Empty;

//DateTime currentDate = DateTime.Now;

//string formattedDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(currentDate);

string batchFormat = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<ows:Batch OnError=\"Continue\">{0}</ows:Batch>";

string methodFormat = "<Method ID=\"{0}\">" +
"<SetList>{1}</SetList>" +
"<SetVar Name=\"Cmd\">Save</SetVar>" +
"<SetVar Name=\"ID\">{2}</SetVar>" +
"<SetVar Name=\"urn:schemas-microsoft-com:office:office#Delete Flag\">{3}</SetVar>" +
"</Method>";

string selectedDate = cutOffDate.SelectedDate.GetDateTimeFormats()[66];

SPList faxLogList = null;

using (SPWeb web = SPControl.GetContextSite(Context).OpenWeb())

{

// Get the list containing the items to update.

if (web.ServerRelativeUrl == "/")

{

faxLogList = (SPList)web.GetList(faxLogListURL);

}
else

{

faxLogList = (SPList)web.GetList(web.ServerRelativeUrl + faxLogListURL);

}
string faxLogListGuid = faxLogList.ID.ToString();
// Query to get the unprocessed items.
SPQuery query = new
SPQuery();

query.RowLimit = 1900;
//query.ViewAttributes = "Scope='Recursive'";

createUtilityObject();

query.ViewFields = _oUtility.BuildViewFieldsXml("ID");

query.Query = "<Where><And><Lt><FieldRef Name='Date' /><Value IncludeTimeValue='False' Type='DateTime'>" + selectedDate + "</Value></Lt><Eq><FieldRef Name='Delete Flag' /><Value Type='Number'>0</Value></Eq></And></Where>";
do

{
SPListItemCollection unprocessedItems = faxLogList.GetItems(query);
// Build the CAML update commands.
for (int i = 0; i < unprocessedItems.Count; i++)

{

int itemID = unprocessedItems[i].ID;

methodBuilder.AppendFormat(methodFormat, itemID, faxLogListGuid, itemID, 1);

}
// Put the pieces together.

batch = string.Format(batchFormat, methodBuilder.ToString());
// Process the batch of commands.
string batchReturn = web.ProcessBatchData(batch);
if (messageFalg == 0 && unprocessedItems.Count > 0)

{

messageFalg = 1;

}

query.ListItemCollectionPosition = unprocessedItems.ListItemCollectionPosition;

} while (query.ListItemCollectionPosition != null);

}

public string BuildViewFieldsXml(string viewFields)

{

const string TEMPLATE = @"<FieldRef Name='{0:S}'/>";
string[] fieldNames = viewFields.Split(',');
StringBuilder sb = new StringBuilder();
foreach (string fieldName in fieldNames)

{

sb.AppendFormat(TEMPLATE, fieldName);

}
return sb.ToString();

}

Take care that the "query.RowLimit = 1900;" for fetching list items is less than 2000 items.