Microsoft的WSS(Windows Sharepoint Services)公开了很多用于访问和管理Sharepoint站点的方法,在调用这些方法时可以通过CAML(Collaborative Application Markup Language)进行一些操作。其中Lists.UpdateListItems()方法提供了用于向Sharepoint List增、删、改数据的方法,但是需要通过CAML语句告诉Sharepoint如何更新数据,有关如何使用CAML以及如何编写CAML进行List数据更新,读者可以参考微软的MSDN文档。
http://msdn.microsoft.com/zh-cn/library/websvclists.lists.updatelistitems.aspx
顺便再给出调用Sharepoint站点的Web Service的地址:
http://Sitename/_vit_bin/lists.asmx?op=UpdateListItems
在使用Lists.UpdateListItems方法时,所使用的用于更新数据的CAML类似于下面这样:
<
Batch
OnError
="Continue"
>
<
Method
ID
="1"
Cmd
="New"
>
<
Field
Name
="Title"
>
Hello
<
Field
>
<
Field
Name
="Document"
>
5
</
Field
>
</
Method
>
<
Method
ID
="2"
Cmd
="New"
>
<
Field
Name
="Title"
>
World
</
Field
>
<
Field
Name
="Document"
>
5
</
Field
>
</
Method
>
</
Batch
>
也就是说我们可以在同一段CAML中批量操作数据。不过最近在实际应用中遇到了一个问题,那就是当我要更新的记录太多,比如20000行,可能我需要写一段特别长的CAML,这个时候当我们在程序中调用Web Service时WSS会给出这样的错误。
Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries.
就是说你所使用的CAML语句太长而被自动截断了。细心观察一下,发现被截断的CAML的前半部分已经成功执行到List中了,而后半部分没有被执行,看来我们需要自己动手来处理这个Bug了。最好的办法就是将过长的CAML分批进行处理,一部分一部分地执行。
1
///
<summary>
2
///
Breaks a larg CAML query into smaller batches to avoid the error "Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries."
3
///
</summary>
4
///
<param name="listService">
The SharePoint list service to execute the CAML against.
</param>
5
///
<param name="listName">
The name of the list to execute the CAML against.
</param>
6
///
<param name="elementLargeBatch">
The CAML batch list of commands to be broken up.
</param>
7
///
<param name="intBatchSize">
The size of batches to use. If unsure use 300, it seems to work fairly well.
</param>
8
///
<returns>
Returns the status of each method block posted through the updates parameter and can
9
///
be assigned to a System.Xml.XmlNode object.
</returns>
10
public
static
XmlNode UpdateListItems(SqlClrSharePointSynchronizer.Lists.Lists listService,
string
listName, XmlElement elementLargeBatch,
int
intBatchSize)
11
{
12
//
calculate useful information
13
int
intMethodCount
=
elementLargeBatch.ChildNodes.Count;
14
int
intBatchCount
=
(
int
)Math.Ceiling((
double
)intMethodCount
/
(
double
)intBatchSize);
15
16
//
prepare xml documents for batches and results
17
XmlDocument xmlDocBatch
=
new
XmlDocument();
18
XmlDocument xmlDocResults
=
new
XmlDocument();
19
XmlElement elementResults
=
xmlDocResults.CreateElement(
"
Results
"
);
20
21
try
22
{
23
//
for each batch
24
for
(
int
intCurrentBatch
=
0
; intCurrentBatch
<
intBatchCount; intCurrentBatch
++
)
25
{
26
int
intMethodStart
=
intCurrentBatch
*
intBatchSize;
27
int
intMethodEnd
=
Math.Min(intMethodStart
+
intBatchSize
-
1
, intMethodCount
-
1
);
28
29
XmlElement elementSmallBatch
=
CreateBatch(xmlDocBatch);
30
31
//
for each method in the batch
32
for
(
int
intCurrentMethod
=
intMethodStart; intCurrentMethod
<=
intMethodEnd; intCurrentMethod
++
)
33
{
34
XmlElement element
=
(XmlElement)elementLargeBatch.ChildNodes[intCurrentMethod];
35
elementSmallBatch.AppendChild(xmlDocBatch.ImportNode(element,
true
));
36
}
37
38
//
execute the batch
39
XmlNode nodeBatchResult
=
listService.UpdateListItems(listName, elementSmallBatch);
40
41
//
add the results of the batch into the results xml document
42
foreach
(XmlElement elementResult
in
nodeBatchResult.ChildNodes)
43
{
44
elementResults.AppendChild(xmlDocResults.ImportNode(elementResult,
true
));
45
}
46
47
//
clean up
48
xmlDocBatch.RemoveAll();
49
}
50
}
51
catch
(SoapException ex)
52
{
53
if
(ex.Detail
==
null
)
54
{
55
throw
;
56
}
57
58
//
copy the exception detail into the Message so it will be available to SQL.
59
throw
new
SoapException(ex.Detail.InnerText, ex.Code, ex.Actor, ex.Detail, ex);
60
}
61
62
return
(XmlNode)elementResults;
63
}
64
65
///
<summary>
66
///
Create the batch element. e.g. <Batch OnError="Continue"></Batch>
67
///
</summary>
68
///
<param name="xmlDoc">
The object of XmlDocument.
</param>
69
///
<returns>
Return the Batch element.
</returns>
70
private
static
XmlElement CreateBatch(XmlDocument xmlDoc)
71
{
72
XmlElement elementBatch
=
xmlDoc.CreateElement(
"
Batch
"
);
73
elementBatch.SetAttribute(
"
OnError
"
,
"
Continue
"
);
74
return
elementBatch;
75
}
我在使用的过程中发现超过600行的数据更新就会出现CAML被截断的情况,所以我干脆将intBatchSize设置为300,超过300行的CAML将会被分批执行。在Web Service中使用CAML经常会遇到这样或那样的问题,查询用的CAML问题更多,不过Microsoft在SP对象中对CAML的支持还是不错的,毕竟是经过封装的,使用起来要顺手许多。