[英]Passing lambda expression to LINQ Include() (SharePoint CSOM)
我尝试搜索,但似乎找不到任何相关答案。 也许是因为我不确定如何提出问题。
我正在编写一个类库,以帮助使用SharePoint的客户端对象模型。 执行查询时,可以指定应加载返回对象的哪些属性,以避免不必要的网络流量。 这是通过Lambda表达式来完成的。
这是一个有效的示例:
public ListItemCollection GetItems(
params Expression<Func<ListItemCollection, object>>[] retrievals)
{
var query = new CamlQuery {...};
ListItemCollection queryResults = _list.GetItems(query);
ReloadClientObject(queryResults, retrievals)
return queryResults;
}
public void ReloadClientObject<T>(T clientObject,
params Expression<Func<T, object>>[] retrievals)
where T : ClientObject
{
_context.Load(clientObject, retrievals);
_context.ExecuteQuery();
}
示例调用:
var items = GetItems(items => items.Include(
item => item.Id,
item => item.DisplayName));
一切都很好。 但是我宁愿返回IEnumerable<ListItem>
而不是ListItemCollection
并且我想传递Expression<Func<ListItem, object>>
类型的参数,而不是Expression<Func<ListItemCollection, object>>
……不介绍用户到ListItemCollection
。 所以我想将Include()
调用移到我的方法的主体中 ……这就是我遇到的问题。
到目前为止,这是我得到的:
public IEnumerable<ListItem> GetItems(
params Expression<Func<ListItem, object>>[] retrievals)
{
var query = new CamlQuery {...};
ListItemCollection queryResults = _list.GetItems(query);
ReloadClientObject(queryResults, items => items.Include(retrievals))
_context.ExecuteQuery();
return queryResults.AsEnumerable();
}
调用示例(更加简洁):
var items = GetItems(item => item.Id, item => item.DisplayName));
但是,这在调用Load()
方法时引发OperationNotSupportedException
。
如有任何指导,我将不胜感激。 谢谢!
直接在查询本身上调用Include
,然后仅使用LoadQuery
而不是Load
来加载查询:
public IEnumerable<ListItem> GetItems(this ClientContext context,
string listName,
params Expression<Func<ListItem, object>>[] retrievals)
{
var query = new CamlQuery();
var queryResults = context.Web.Lists.GetByTitle(listName)
.GetItems(query)
.Include(retrievals);
context.LoadQuery(queryResults);
context.ExecuteQuery();
return queryResults;
}
由于这对您不起作用(根据您的评论 ,您需要利用分页功能),我们需要做更多的工作。
因此,我们在这里要做的是创建一个Expression<Func<ListItemCollection, ItemSelector, object>>
,它将获取一个集合,一个选择器,并将其映射到一个对象。 这里的ItemSelector
是通过using ItemSelector = Expression<Func<ListItem, object>>;
(因为尝试使用Expression<Func<ListItemCollection, Expression<Func<ListItem, object>>, object>>
只是残酷和不寻常的惩罚)。 我们可以这样定义它:
Expression<Func<ListItemCollection, ItemSelector, object>> includeSelector =
(items, selector) => items.Include(selector);
现在我们可以做的是编写一个Apply
方法,该方法可以使用带有两个参数的函数的表达式,用常量替换第二个参数的所有实例,从而创建一个参数少的方法。 这是Apply
方法的定义:
public static Expression<Func<T1, TResult>> Apply<T1, T2, TResult>(
this Expression<Func<T1, T2, TResult>> expression,
T2 value)
{
return Expression.Lambda<Func<T1, TResult>>(
expression.Body.Replace(expression.Parameters[1],
Expression.Constant(value))
, expression.Parameters[0]);
}
这使用此帮助程序方法将一个表达式的所有实例替换为另一个:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
因此,现在我们可以使用这个includeSelector
表达式,并针对数组中的每个项目选择器,将该选择器应用于此函数。 将这些结果放入数组后,我们得到一个Expression<Func<ListItemCollection, object>>[]
,这正是我们需要传递给Load
。
呼。 这是实际执行此操作的最终代码:
public static IEnumerable<ListItem> GetItems(this ClientContext context,
string listName,
params Expression<Func<ListItem, object>>[] retrievals)
{
var query = new CamlQuery();
var queryResults = context.Web.Lists.GetByTitle(listName)
.GetItems(query);
Expression<Func<ListItemCollection, ItemSelector, object>> includeSelector =
(items, selector) => items.Include(selector);
context.Load(queryResults, retrievals
.Select(selector => includeSelector.Apply(selector))
.ToArray());
context.ExecuteQuery();
return queryResults;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.