如何为不同的 ContentTypes 查询 SharePoint 列表?

How can I query a SharePoint List for different ContentTypes?

我正在开发一个控制台应用程序以使用 .NET client object model of the SharePoint Server 2013 Client Components SDK:

从 SharePoint 2013 服务器提取数据
using (var clientContext = new ClientContext(SharePointUrl))
{
    var list = clientContext.Web.Lists.GetByTitle(listName);
    var camlQuery = CamlQuery.CreateAllItemsQuery();
    var listItems = list.GetItems(camlQuery);
    clientContext.Load(listItems, GetFields(fields));
    clientContext.ExecuteQuery();
    return listItems.ToList();
}

这是我为构建我请求的字段列表而调用的 GetFields() 方法:

private Expression<Func<ListItemCollection, object>>[] GetFields(IEnumerable<string> fields)
{
    return fields
        .Select(field => (Expression<Func<ListItemCollection, object>>)(items => items.Include(item => item[field])))
        .ToArray();
}

有一个大列表 - "Demands" - 给我一些 User-type 字段带来麻烦:每当我将这些作为需要检索的字段时 通过 GetFields() 方法(见上文), 我得到 the infamous "Value does not fall within the expected range." error。例如,"DTBusinessResponsible"可以检索,但"DTSupplyLeadResponsible"不能检索。

我被告知 "Demands" 包含两个 ContentTypes"Demand""I-Demand"。给我问题的字段是 "I-Demand".

独有的

我不知道如何检索这些有问题的字段。我尝试检索 "Demand""I-Demand"ContentType 并将它们添加到 list:

var demandContentType = clientContext.Web.ContentTypes.GetById(contentTypeByName["Demand"]);
var iDemandContentType = clientContext.Web.ContentTypes.GetById(contentTypeByName["I-Demand"]);

list.ContentTypesEnabled = true;
list.ContentTypes.AddExistingContentType(demandContentType);
list.ContentTypes.AddExistingContentType(iDemandContentType);

但这没有用,我收到一条错误消息,告诉我两个 ContentType 都已经存在。


(请注意 I have already asked this over at the SharePoint SE,但我在那里没有得到任何有用的回复。)


编辑:让我们简化问题。

我需要从名为 "Demands" 的 SharePoint 列表中检索多个字段。为此,我将需要的字段添加为 Expression<Func<T, object>>[].

但是,我需要检索的一些字段是 "I-Demand" 独有的,它是 "Demands" 列表的 ContentTypes 之一。每当我添加这些字段时,查询都会崩溃并生成 the infamous "Value does not fall within the expected range." error。我可以轻松检索属于 "Demand" 内容类型的数十个其他字段。

如何检索 "I-Demand" 内容类型独有的那些字段?


编辑:这是不起作用的...

var fields = new List<string>
{
    "DTBusinessRequestor",
    "DTBusinessResponsible",
    "DTBusinessSponsor",
    "DTDemandAccountable",
    "DTIICTBusinessAnalyst",
    "DTSupplyAccountable",
    "DTSupplyLeadResponsible",
    "Title",
    "Author",
    "Created",
    "Editor",
    "Modified",
    "ID"
};

var viewXml = new StringBuilder();

viewXml.AppendLine("<ViewFields>");
foreach (var field in fields)
{
    viewXml.AppendFormat("<FieldRef Name=\"{0}\" />", field).AppendLine();
}
viewXml.AppendLine("</ViewFields>");

var camlQuery = new CamlQuery { ViewXml = string.Format("<View>{0}</View>", viewXml) };

List<ListItem> results;

using (var clientContext = GetClientContext())
{
    var list = clientContext.Web.Lists.GetByTitle("Demands");
    var listItems = list.GetItems(camlQuery);
    clientContext.Load(listItems);
    clientContext.ExecuteQuery();
    results = listItems.ToList();
}

var validNames = new HashSet<string>();

foreach (var listItem in results)
{
    foreach (var field in fields)
    {
        object value;
        if (!listItem.FieldValues.TryGetValue(field, out value))
            continue;

        if (value != null)
        {
            validNames.Add(field);
        }
    }
}

当我执行上面的代码时:

这比我得到的结果稍微好一点,因为 "Author" 和 "Editor" 有问题。其余字段 return 为空,但这可能是因为十个结果没有值。但是,我无法与 SharePoint 中的值进行比较,因为 returned 的十个标题是 而不是 要求 table...


我尝试通过在 viewXml.AppendLine("</ViewFields>");:

之后添加此行来按 ContentType 进行过滤
viewXml.AppendLine("<Query><Where><Eq><FieldRef Name='ContentType' /><Value Type='Computed'>I-Demand</Value></Eq></Where></Query>");

结果:results 不包含条目。


我删除了 ContentType 过滤器并更改为:

var camlQuery = new CamlQuery { ViewXml = string.Format("<View>{0}</View>", viewXml) };

对此:

var camlQuery = CamlQuery.CreateAllItemsQuery();
camlQuery.ViewXml = string.Format("<View>{0}</View>", viewXml);

与原来相同的结果:十个项目,相同的字段。

然后我删除这一行:

camlQuery.ViewXml = string.Format("<View>{0}</View>", viewXml);

结果:160 项,"DTBusinessRequestor"、"DTBusinessResponsible"、"DTBusinessSponsor" 和 "DTIICTBusinessAnalyst" BUT[=121= 的值] "Author" 和 "Editor" 没有结果——所以我回到了起点。


我还没有找到一个单一的解决方案,它 return 为需求列表中的所有 160 个条目加上 "Author" 和 "Editor" 以及 [=141] 的值=]、"DTSupplyAccountable" 和 "DTSupplyLeadResponsible"(如适用)。

每当我添加 <Query><Where><Eq><FieldRef Name='ContentType' /><Value Type='Computed'>I-Demand</Value></Eq></Where></Query> 我得到零结果。

经过多次试验和错误,我设法让它工作:

var fields = new List<string>
{
    "DTSupplyAccountable",
    "DTSupplyLeadResponsible",
    "Author",
    "Editor",
    "ID"
};

var viewXml = new StringBuilder();

viewXml.AppendLine("<ViewFields>");
foreach (var field in fields)
{
    viewXml.AppendFormat("<FieldRef Name=\"{0}\" />", field).AppendLine();
}
viewXml.AppendLine("</ViewFields>");

viewXml.AppendLine("<Query><Where><Eq><FieldRef Name=\"ContentType\" /><Value Type=\"Computed\">I-Demand</Value></Eq></Where></Query>");

var camlQuery = new CamlQuery {ViewXml = string.Format("<View Scope=\"RecursiveAll\">{0}</View>", viewXml)};

List<ListItem> results;

using (var clientContext = GetClientContext())
{
    var list = clientContext.Web.Lists.GetByTitle("Demands");
    var listItems = list.GetItems(camlQuery);
    clientContext.Load(listItems);
    clientContext.ExecuteQuery();
    results = listItems.ToList();
}

这里是关键细节(除了显而易见的 <Where><Eq><FieldRef Name=\"ContentType\" /><Value Type=\"Computed\">I-Demand</Value></Eq></Where>):

ViewXml = string.Format("<View Scope=\"RecursiveAll\">{0}</View>", viewXml)

事实上,Scope=\"RecursiveAll\"才是最重要的部分。我在使用 Fiddler 查看发送到服务器的各种请求时遇到了它,并注意到所有工作调用都包括以下内容:

<Property Name="ViewXml" Type="String">&lt;View Scope="RecursiveAll"&gt;&#xD;
    &lt;Query&gt;&#xD;
    &lt;/Query&gt;&#xD;
&lt;/View&gt;</Property>

一时兴起,我决定将 Scope="RecursiveAll" 添加到我的代码中,结果成功了。

构造两个单独的 caml 查询,每个查询指定一种或另一种内容类型。然后,获取结果,并将它们读入您自己的数据对象(例如 POCO 对象——普通旧 C# 对象)。一旦它们在您的 Poco 数据对象中,您就可以使用 Linq 合并数据集,然后您可以使用这些数据集。

您可以编写通用方法将 ListItem 数据读取到 POCO 对象中,因此这是一种可重用的方法,可在您遇到此问题时使用。