Так вот мне однажды пришлось писать систему массового удаления данных из разных списков сайта под управлением Sharepoint 2010. Решения через SPListItem.Delete(), SPList.Items.DeleteItem(idx) и даже SPList.Items.DeleteItemById(itemId) давали жуткие показатели времени работы (удаление 100 элементов за 60000(!)-120000(!) секунд). И тут я вспомнил про волшебную возможность SPWeb.ProcessBatchData которая позволяет выполнять CAML запросы напрямую, минуя слой объектной модели.( Забегая вперед скажу что такое решение мне дало производительность в районе ~650сек на 5000 записей.)
Что-ж покопав документацию на сайте Microsoft (http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.processbatchdata.aspx) я подумал что все тривиально и создал свое первое решение. Радостно посапывая я ткнул в кнопочку деплой и запустил свой код. Как ни странно код рухнул. Не буду описывать процесс поиска ошибки и сразу скажу: Разбивайте свои запросы на пачки! Не больше 180 запросов в одном теге
Поправив код и сделав разбиение на пачки я запустил все снова. Как и ожидалось пепелац не взлетел. А я поимел совершенно смутившую меня ошибку ArgumentException. Тут надо отметить удивительную абсолютную невразумительности сообщений Sharepoint об ошибках.
Вот к примеру как выглядел мой Expcetion
Exception type: ArgumentException [Message] = "Поиски google ничего вразумительного не дали. Правда у меня зародилось смутное сомнение в том что я что-то не так делаю с форматом CAML запроса. Я даже не могу сказать что мена натолкнуло на эту мысль, интуиция если только.0x80070057 " [ParamName] = "" [Data] = "System.Collections.ListDictionaryInternal" [TargetSite] = "Void GetListItemDataWithCallback2(Microsoft.SharePoint.Library.IListItemSqlClient, System.String, System.String, System.String, System.String, Microsoft.SharePoint.Library.SAFEARRAYFLAGS, Microsoft.SharePoint.Library.ISP2DSafeArrayWriter, Microsoft.SharePoint.Library.ISPDataCallback, Microsoft.SharePoint.Library.ISPDataCallback, Microsoft.SharePoint.Library.ISPDataCallback, Microsoft.SharePoint.Library.ISPDataCallback, Microsoft.SharePoint.Library.ISPDataCallback, Boolean ByRef)" [StackTrace] = " в Microsoft.SharePoint.Library.SPRequestInternalClass.GetListItemDataWithCallback2(IListItemSqlClient pSqlClient, String bstrUrl, String bstrListName, String bstrViewName, String bstrViewXml, SAFEARRAYFLAGS fSafeArrayFlags, ISP2DSafeArrayWriter pSACallback, ISPDataCallback pPagingCallback, ISPDataCallback pPagingPrevCallback, ISPDataCallback pFilterLinkCallback, ISPDataCallback pSchemaCallback, ISPDataCallback pRowCountCallback, Boolean& pbMaximalView) в Microsoft.SharePoint.Library.SPRequest.GetListItemDataWithCallback2(IListItemSqlClient pSqlClient, String bstrUrl, String bstrListName, String bstrViewName, String bstrViewXml, SAFEARRAYFLAGS fSafeArrayFlags, ISP2DSafeArrayWriter pSACallback, ISPDataCallback pPagingCallback, ISPDataCallback pPagingPrevCallback, ISPDataCallback pFilterLinkCallback, ISPDataCallback pSchemaCallback, ISPDataCallback pRowCountCallback, Boolean& pbMaximalView) в Microsoft.SharePoint.SPListItemCollection.EnsureListItemsData() в Microsoft.SharePoint.SPListItemCollection.GetEnumerator() в MRR.DVIZ.Workflows.ClearFormsWorkflow.ClearFormsWorkflow.FindRunning(Object sender, EventArgs e) в System.Workflow.ComponentModel.Activity.RaiseEvent(DependencyProperty dependencyEvent, Object sender, EventArgs e) в System.Workflow.Activities.CodeActivity.Execute(ActivityExecutionContext executionContext) в System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext) в System.Workflow.ComponentModel.ActivityExecutor`1.Execute(Activity activity, ActivityExecutionContext executionContext) в System.Workflow.ComponentModel.ActivityExecutorOperation.Run(IWorkflowCoreRuntime workflowCoreRuntime) в System.Workflow.Runtime.Scheduler.Run()" [HelpLink] = "" [Source] = ""
Не буду рассказывать свои танцы вокруг подбора нового формата. скажу лишь что:
<Batch OnError="Continue"> <SetList>b15da36c-6ed8-4fe1-bbcc-89a1996c1dbe</SetList> <Method ID="1" Cmd="Delete"><Field Name="ID">638</Field></Method> <Method ID="2" Cmd="Delete"><Field Name="ID">639</Field></Method> <Method ID="3" Cmd="Delete"><Field Name="ID">648</Field></Method> <Method ID="4" Cmd="Delete"><Field Name="ID">649</Field></Method> <Method ID="5" Cmd="Delete"><Field Name="ID">650</Field></Method> <Method ID="6" Cmd="Delete"><Field Name="ID">651</Field></Method> <Method ID="7" Cmd="Delete"><Field Name="ID">652</Field></Method> <Method ID="8" Cmd="Delete"><Field Name="ID">653</Field></Method> <Method ID="9" Cmd="Delete"><Field Name="ID">654</Field></Method> <Method ID="10" Cmd="Delete"><Field Name="ID">655</Field></Method> <Method ID="11" Cmd="Delete"><Field Name="ID">656</Field></Method> <Method ID="12" Cmd="Delete"><Field Name="ID">657</Field></Method> </Batch>не красиво но не правильно. Надо:
<?xml version="1.0" encoding="UTF-8"?> <Batch OnError="Return">
<Method ID="1,Del"><SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">20</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="2,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">21</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="3,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">22</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="4,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">23</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="5,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">24</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="6,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">25</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="7,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">26</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="8,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">27</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="9,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">28</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="10,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">29</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="11,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">30</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> <Method ID="12,Del"> <SetList Scope="Request">465ebd0a-d6d4-4736-81d7-27cac86e8952</SetList> <SetVar Name="ID">31</SetVar><SetVar Name="Cmd">Delete</SetVar></Method> </Batch>Код создающий такую структуру и выполняющие его в пачках (listMods - IEnumerable
List<string> listBatch = new List(); StringBuilder batch = new StringBuilder(); StringBuilder log = new StringBuilder(); if (!string.IsNullOrEmpty(listTitle) && listMods != null && listMods.Count > 0) { log.AppendFormat("Очистка списка {0}. Удалеине {1} элементов.", listTitle, listMods.Count); batch.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); batch .AppendLine("<Batch OnError=\"Return\">"); int batchSize = 0; foreach (int toDelete in listMods) { batchSize++; batch.AppendFormat( "<Method ID=\"{0},Del\"> {2} <SetVar Name=\"ID\">{1}</SetVar><SetVar Name=\"Cmd\">Delete</SetVar></Method>", batchSize, toDelete, string.Format("<SetList Scope=\"Request\">{0}</SetList>", queryRoot.Lists[listTitle].ID)) .AppendLine(); if (batchSize >= 100) { batch.AppendLine ("</Batch>"); listBatch.Add(batch.ToString()); batchSize = 0; batch = new StringBuilder(batch.Capacity); batch.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); batch.AppendLine("<Batch OnError=\"Return\">"); } } batch.AppendLine("</Batch>"); listBatch.Add(batch.ToString()); foreach (string cmd in listBatch) { try { log.Append("Исполнение запроса :").AppendLine().Append(cmd).AppendLine(); string cmd1 = cmd; SPSite siteColl = currentSite; SPWeb site = currentWeb; SPSecurity.RunWithElevatedPrivileges(delegate() { using ( SPSite ElevatedsiteColl = new SPSite(siteColl.ID)) { using ( SPWeb ElevatedWeb = ElevatedsiteColl.OpenWeb( site.ID)) { ElevatedWeb.ProcessBatchData(cmd1); } } }); } catch (Exception e) { var ex = new ApplicationException("Ошибка при исполнении комманды: " + cmd, e); throw ex; } } }
Комментариев нет:
Отправить комментарий