In this example, I take the value of a text field "Date_x0020_of_x0020_Birth", turn this into a DateTime value, and then use this to update the the value of a DateTime field "Date_x0020_of_x0020_Birth0". Obviously this section of the code will not be particularly generalisable, however I have left in all the code in order to show you the principles involved, and hopefully you will find it useful in building your own batch routines.
In order to get it running, you must create a reference to the Sharepoint Lists web service and call it "ListsService". The SDK shows you how to do this, and is available here: http://www.microsoft.com/downloads/en/details.aspx?FamilyId=6D94E307-67D9-41AC-B2D6-0074D6286FA9&displaylang=en
The GetListItems() method of the Lists web service is used to query the data, and the UpdateListItems() method is used to update items.
I used an XPathNavigator object to filter only rows that contain data. You could just loop through the nodes if you prefer.
CAML is used to query the Sharepoint list and look for items where the "Date_x0020_of_x0020_Birth" is null. Tailor this to your own needs.
For CAML reference, look at the MSDN article here: http://msdn.microsoft.com/en-us/library/lists.lists.getlistitems%28v=office.12%29.aspx
The CAML update command uses owshiddenversion, and so is useful in situations where versioning is enabled. Lists without versioning do not need this field.
If the UpdateListItems command returns an error, in my experience this is usually caused by using an incorrect internal name for a field. You may need to add the URI prefix "urn:schemas-microsoft-com
Full code:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SharePoint; using Microsoft.SharePoint.Utilities; using System.IO; using System.Xml; using System.Xml.XPath; using System.Net; namespace BatchUpdateDOB { class Program { static string LogFile = "D:\\BatchUpdateLog.txt"; // Log file location static void Main(string[] args) { try { StatusMessage("Writing log file to: " + LogFile); // 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 fieldToReference = "Date_x0020_of_x0020_Birth"; string fieldToUpdate = "Date_x0020_of_x0020_Birth0"; string batchFormat = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<ows:Batch OnError=\"Return\">{0}</ows:Batch>"; // CAML update command for web service string methodFormat = "<Method ID=\"1\" Cmd=\"Update\">" + "<Field Name=\"ID\">{1}</Field>" + "<Field Name=\"FileRef\">{2}</Field>" + "<Field Name=\"owshiddenversion\">{3}</Field>" + "<Field Name=\"{4}\">{5}</Field>" + "</Method>"; //UPDATE THESE TWO LINES TO SITE SPECIFIC VALUES string siteName = "http://<your site root uri here>"; string listName = "<your list name here>"; //string viewName = ""; // No need for viewname int rowLimit = 10; //The number of items returned per batch (not used) StatusMessage("Processing " + siteName + ", List: " + listName + "..."); //Web service setup //ListService should be the name of your web service reference ListService.Lists listService = new ListService.Lists(); listService.Credentials = System.Net.CredentialCache.DefaultCredentials; listService.Url = siteName + "/_vti_bin/Lists.asmx"; //Build CAML query to search for records XmlDocument camlDocument = new XmlDocument(); XmlNode queryNode = camlDocument.CreateElement("Query"); //queryNode.InnerXml = "<Where><IsNull><FieldRef Name='Date_x0020_of_x0020_Birth'/></IsNull></Where>"; //queryNode.InnerXml = "<OrderBy><FieldRef Name='Country' /></OrderBy>"; XmlNode viewFieldsNode = camlDocument.CreateElement("ViewFields"); viewFieldsNode.InnerXml = "<FieldRef Name='Title' />" + "<FieldRef Name='owshiddenversion' />" + "<FieldRef Name='" + fieldToReference + "' />" + "<FieldRef Name='" + fieldToUpdate + "' />" + "<FieldRef Name='FileRef' />"; XmlNode queryOptionsNode = camlDocument.CreateElement("QueryOptions"); queryOptionsNode.InnerXml = "<ViewAttributes Scope=\"Recursive\" /><IncludeMandatoryColumns>True</IncludeMandatoryColumns>"; // Get the GUID of the list XmlNode ndListView = listService.GetListAndView(listName, ""); string listGuid = ndListView.ChildNodes[0].Attributes["Name"].Value; string listVersion = ndListView.ChildNodes[0].Attributes["Version"].Value; // Run the CAML query XmlNode resultNode = listService.GetListItems(listGuid, null, queryNode, viewFieldsNode, rowLimit.ToString(), queryOptionsNode, null); // If you want to put a rowlimit on, then use this command: //XmlNode resultNode = listService.GetListItems(listGUID, null, queryNode, viewFieldsNode, rowLimit.ToString(), queryOptionsNode, null); long totalRecords = 0; //string itemDOB; string dob; DateTime dtDOB; string owsHiddenVersion; string fullFileRef; string owsFileRef; // Create xpath navigator XPathNavigator navigator = resultNode.CreateNavigator(); // Set up namespace manager for XPath XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable); ns.AddNamespace("rs", "urn:schemas-microsoft-com:rowset"); ns.AddNamespace("z", "#RowSetSchema"); // Select only the z:row nodes XmlNodeList nodes = resultNode.SelectNodes("//*[name()='z:row'] ", ns); // Iterate through the returned nodes foreach (XmlNode node in nodes) { string itemID = node.Attributes["ows_ID"].InnerText; try { dob = node.Attributes["ows_" + fieldToReference].InnerText; owsHiddenVersion = node.Attributes["ows_" + "owshiddenversion"].InnerText; owsFileRef = node.Attributes["ows_" + "FileRef"].InnerText; // For some reason my data is returned in the format: 2#/listname/path, // where 2 seems to be an id or version number, and listname/path points to the file in relation to the root site. // The following strips that out and gets the full path (required for the CAML update) fullFileRef = siteName + "/" + owsFileRef.Remove(0, owsFileRef.IndexOf("#") + 1); } catch(Exception ex) { // Ignore this row //StatusMessage("ERROR: Cannot process: " + node.InnerXml.ToString() + ": " + ex.Message); // If you want to see why, uncomment continue; } if (String.IsNullOrEmpty(dob)) continue; // Ignore list items with no value dtDOB = Convert.ToDateTime("01/01/1900"); // Set default value for dates before 1900 // Convert date text to DateTime try { dtDOB = Convert.ToDateTime(dob); } catch (Exception ex) { StatusMessage("Cannot process: " + siteName + "/" + fullFileRef + " DOB: " + dob + ". Setting to 01/01/1900. " + ex.Message); } MessageToFile("Processing: " + fullFileRef + ", DOB = " + dob); methodBuilder.AppendFormat(methodFormat, listGuid, itemID, fullFileRef, owsHiddenVersion, fieldToUpdate, SPUtility.CreateISO8601DateTimeFromSystemDateTime(dtDOB)); // Put the pieces together. batch = string.Format(batchFormat, methodBuilder.ToString()); //MessageToFile(batch); //Build CAML command to update using Web service XmlDocument xmlDoc = new XmlDocument(); XmlElement elBatch = xmlDoc.CreateElement("Batch"); elBatch.SetAttribute("OnError", "Continue"); elBatch.SetAttribute("ListVersion", listVersion.ToString()); elBatch.SetAttribute("ViewName", ""); elBatch.InnerXml = methodBuilder.ToString(); // This line runs the CAML update command XmlNode updatesResponse = listService.UpdateListItems(listGuid, elBatch); if (updatesResponse.FirstChild.FirstChild.InnerText != "0x00000000") throw new Exception("Could not update properties.\n\n" + updatesResponse.InnerText + "\n" + updatesResponse.FirstChild.FirstChild.InnerText); StatusMessage(fullFileRef + " processed..."); totalRecords++; } StatusMessage("Completed - " + totalRecords + " processed."); } catch (Exception exp) { StatusMessage("ERROR: " + exp.Message); } } /// <summary> /// Display a message /// </summary> /// <param name="myMessage"></param> static void StatusMessage(string myMessage) { Console.WriteLine(myMessage.ToString()); MessageToFile(myMessage); } /// <summary> /// Write message to the log file /// </summary> /// <param name="myMessage"></param> static void MessageToFile(string myMessage) { using (StreamWriter sw = new StreamWriter(LogFile, true)) { DateTime now = DateTime.Now; sw.WriteLine(now.ToString() + ": " + myMessage.ToString()); } } } }
Consider reusing: http://alexeydev.blogspot.com/2011/11/sharepoint-batch-add-update-and-delete.html
ReplyDeleteThanks Alexey, that's pretty cool :)
ReplyDelete