我使用CSOM.NET从Project Server 2013加载任务对象,并且我需要
- 过滤任务,以便只返回其中的一个子集,以及
- 只加载用户在运行时指定的任务列的子集
我发现这篇文章展示了如何加载一组动态列,它很好地满足了我的第二个需求。然而,我无法找到一种可行的LINQ语法来组合列选择和行筛选。
在下面的示例中,我只需要为摘要任务加载那些"行"(其中t.IsSummary为true),并且我只想加载名称、开始和完成列。
参考文章中的以下代码只加载了我需要的三列:
foreach (string fieldName in new List<string>(){"Name","Start","Finish"});
{
ctx.Load(ctx.Tasks,c => c.Include(t => t[fieldName]));
}
ctx.ExecuteQuery();
但是,当我试图在唯一对我有意义的语法中组合where()和include()时,我在foreach循环的第二次迭代中得到InvalidQueryExpressionException:"不支持查询表达式。"
foreach (string fieldName in new List<string>(){"Name","Start","Finish"});
{
ctx.Load(ctx.Tasks,
c => c.Where(t => t.IsSummary),
c => c.Include(t => t[fieldName])
);
}
ctx.ExecuteQuery();
如果我颠倒的顺序,我会得到同样的错误,其中和包含子句。如果我将where子句拉到字段名称循环之外,并使其成为一个单独的Load调用,则摘要任务行筛选会起作用,但我会丢失任务字段的动态选择。CSOM的LINQ中必须有一个满足这两个要求的语法。执行这种类型的查询的正确语法是什么?
以下示例演示如何在SharePoint CSOM API中应用选择和筛选运算符:
var list = ctx.Web.Lists.GetByTitle(listTitle);
var items = list.GetItems(CamlQuery.CreateAllItemsQuery());
var result = ctx.LoadQuery(items.Where(i => (bool)i["IsSummary"]).Include(i => i["Name"], i => i["Start"], i => i["Finish"]));
ctx.ExecuteQuery();
foreach (var item in result)
{
Console.WriteLine(item["Name"]);
}
因此,我相信以下表达式在Project Server CSOM API中是受支持的:
var result = ctx.LoadQuery(ctx.Tasks.Where(t => (bool)t["IsSummary"]).Include(t => i["Name"], t => t["Start"], t => t["Finish"]));
ctx.ExecuteQuery();
我自己通过使用表达式树来回答这个问题,通过表达式树可以过滤一组行,并根据仅在运行时已知的参数选择一组列。在下面的示例中,我模拟在运行时发现我需要过滤IsSummary列上的任务,并且我应该只检索Id、Name和Start这五列,IsSubProject,Finish。这是代码:
using System.Linq.Expressions;
// Input parms discovered at runtime
string filterColumnName = "IsSummary";
List<string> columnNames = new List<string>(
new[] { "Id", "Name", "Start", "IsSubProject", "Finish" });
// Get the client object for the Published Project matching projGuid
ctx.Load(ctx.Projects, c => c.Where(p => p.Id == projGuid));
ctx.ExecuteQuery();
PublishedProject proj = ctx.Projects.Single();
// Compute the expression tree for filtering the task rows
var taskParm = Expression.Parameter(typeof(PublishedTask), "t");
var predicate = Expression.PropertyOrField(taskParm, filterColumnName);
var filterExpression = Expression.Lambda<
Func<PublishedTask, bool>>(predicate, taskParm);
// Dynamically generate expression tree for each column to be included
var colSelectionList = new List<Expression<
Func<PublishedTask, object>>>();
foreach (var colName in columnNames)
{
var fldExpr = Expression.PropertyOrField(taskParm, colName);
var fldAsObjExpr = Expression.Convert(fldExpr, typeof(object));
var colSelectorExpr = Expression.Lambda<
Func<PublishedTask, object>>(fldAsObjExpr, taskParm);
colSelectionList.Add(colSelectorExpr);
}
// Create query using LoadQuery (Load does not work here)
var taskList = ctx.LoadQuery(proj.Tasks
.Where(filterExpression)
.Include(colSelectionList.ToArray())
);
// Execute the query
ctx.ExecuteQuery();
// taskList now contains just the filtered rows and selected columns
我希望这能帮助其他坚持使用CSOM为Project Server做这件事的人。我发现这两个参考资料很有帮助:在MSDN和匈牙利SharePoint Geek 的第二人生
Jim