繁体   English   中英

将Lambda表达式传递给LINQ Include()(SharePoint CSOM)

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM