背景:
我正在尝试创建一个C#命令行实用程序,从特定网站集中可能存在的列表中提取列表项信息。我试图从中提取的所有列表都是从ID为10003的特定模板(自定义模板)中创建的。
当权者仍在决定这件事应该多久运行一次,但我预计每隔几分钟左右就会有一个答案,所以我需要的执行时间不超过一分钟,我认为这在我目前的方法下是无法实现的。
我有一个网站集,有7个直系子网站和大约200个后代子网站,这些列表可能出现在其中任何一个网站中。大多数实例将只有几个项目,但其中一些实例将有几千个。我预计总共会有10-20k个结果。
这个实用程序将在远程服务器上运行,在内部我们更喜欢使用CSOM而不是其他。我熟悉SP web部件的开发,但这是我第一次需要使用CSOM。
该农场是一个SP 2010 On Prem,有3个WFE,更新了最新的CU。
即时问题:
- 我在调用上下文时抛出"无法完成此操作"。ExecuteQuery(),我不知道为什么
- 我意识到我可能过度调用上下文了。ExecuteQuery(),并想知道加载所有这些列表的更好方法
- 当我需要根的所有后代时,我当前的代码只获得直接的子网
代码:
我目前的尝试如下:
using (var ctxt = new ClientContext(url))
{
var webs = ctxt.Web.Webs;
ctxt.Load(webs);
ctxt.ExecuteQuery();
var allItems = new List<ListItem>();
foreach (var web in webs)
{
ctxt.Load(web.Lists);
ctxt.ExecuteQuery();
foreach (var list in web.Lists)
{
if (list.BaseTemplate == 10003)
{
ctxt.Load(list);
ctxt.ExecuteQuery();
var items = list.GetItems(query);
ctxt.Load(items);
ctxt.ExecuteQuery(); // <- **throws "Cannot complete this action." on first iteration of loop.**
allItems.AddRange(items);
}
}
}
results = allItems.Select(ConvertToNeededResultType).ToList();
}
查询看起来像:
<View Scope='RecursiveAll'>
<Webs Scope='SiteCollection' /> <!--**If I omit this line, I get a CollectionNotInitialized exception on allitems.AddRange(items)**--/>
<Lists ServerTemplate='10003' />
<Query>
<OrderBy>
<FieldRef Name='CreatedOn' Ascending='False' />
</OrderBy>
<Where>
<And>
<Eq>
<FieldRef Name='FSObjType' />
<Value Type='Integer'>0</Value>
</Eq>
<Geq>
<FieldRef Name='CreatedOn'/>
<Value Type='DateTime' IncludeTimeValue='FALSE'>{0}</Value>
</Geq>
</And>
</Where>
<Query>
<ViewFields>
<FieldRef Name='Title' Nullable='TRUE' />
<FieldRef Name='URL' Nullable='TRUE' />
<FieldRef Name='CreatedOn' Nullable='TRUE' />
<FieldRef Name='Category' Nullable='TRUE' />
<FieldRef Name='Attachments' Nullable='TRUE' />
<FieldRef Name='ID' />
<ProjectProperty Name='Title' />
<ListProperty Name='Title' />
</ViewFields>
</View>
其中{0}包含一个日期,该日期是我要返回列表的天数,格式为:"yyyy-MM-ddTHH:MM:ssZ"
我要查找的内容:
我正在寻找关于如何用我当前的代码解决上面列举的特定问题的建议,或者关于如何更有效地实现相同结果的示例的建议。
我从来没有弄清楚"无法完成此操作"这一点,但我已经用另一种方式解决了需求:我需要两种方法,递归来抓取网页和检索项目:
public static IEnumerable<WebLocation> GetWebLocations(string rootWebUrl)
{
List<WebLocation> results;
using (var cntxt = new ClientContext(rootWebUrl))
{
var web = cntxt.Web;
cntxt.Load(web, w => w.Webs, w => w.Id, w => w.ServerRelativeUrl, w => w.Lists);
cntxt.ExecuteQuery();
results = GetWebLocations(cntxt, web, Guid.Empty);
}
return results;
}
private static List<WebLocation> GetWebLocations(ClientContext cntxt, Web currentWeb, Guid parentId)
{
var results = new List<WebLocation>();
var currentId = currentWeb.Id;
var url = currentWeb.ServerRelativeUrl;
var location = new WebLocation { ParentSiteID = parentId, SiteID = currentId, WebUrl = url, HotLinksItems = new List<HotLinksItem>() };
foreach (var list in currentWeb.Lists)
{
cntxt.Load(list, l => l.BaseTemplate, l => l.RootFolder.ServerRelativeUrl);
cntxt.ExecuteQuery();
if (list.BaseTemplate == 10003)
{
var itemCollection =
list.GetItems(new CamlQuery
{
ViewXml = "<View Scope='RecursiveAll'><ViewFields><FieldRef Name='Title' Nullable='TRUE' /><FieldRef Name='ID' /><ProjectProperty Name='Title' /><ListProperty Name='Title' /></ViewFields></View>"
});
cntxt.Load(itemCollection);
cntxt.ExecuteQuery();
foreach (var item in itemCollection)
{
var hotlink = new HotLinksItem
{
Title = item["Title"] != null ? item["Title"].ToString() : null,
ID = item["ID"] != null ? item["ID"].ToString() : null,
};
location.HotLinksItems.Add(hotlink);
}
}
}
}
我仍然觉得我打了太多的电话,但用子选择来减少电话对我的表现有很大帮助。总而言之,对我的农场来说,这件事只需要30秒多一点。(在削减退货项目之前,它大约在3分钟内运行。)