如何获取返回要调用的表达式而不是解释为表达式的函数

本文关键字:表达式 解释 函数 调用 何获取 获取 返回 | 更新日期: 2023-09-27 18:15:37

我正在使用SharePoint CSOM。它使用基于表达式的查询语言来指定要检索的数据。我正在研究的系统进行了几个不同的查询,但它们都需要包括某些用于安全检查的数据。例如:

using (ClientContext context = new ClientContext(weburl))
{
    var subwebs = context.Web.GetSubwebsForCurrentUser(new SubwebQuery());
    context.Load(subwebs,
        webs => webs.Include(
            // BEGIN security-related boilerplate
            web => web.RoleAssignments.Include(
                ra => ra.Member,
                ra => ra.RoleDefinitionBindings.Include(
                    rdb => rdb.BasePermissions,
                    rdb => rdb.RoleTypeKind
                )
            ),
            // END security-related boilerplate
            web => web.Title,
            web => web.ServerRelativeUrl));
    context.ExecuteQuery();
}

我试图避免在每个需要的地方复制/粘贴与安全相关的表达式。对Include方法的检查表明,它接受一个表达式列表作为参数:

IQueryable<TSource> Include<TSource>(
    this IQueryable<TSource> clientObjects, 
    params Expression<Func<TSource, object>>[] retrievals) 
    where TSource : ClientObject
所以我创建了一个扩展方法,它返回所需的表达式:
public static Expression<Func<T, object>> GetSecurityExpression<T>(this ClientObjectCollection<T> list) 
    where T : SecurableObject
{
    return x => x.RoleAssignments.Include(
        ra => ra.Member,
        ra => ra.RoleDefinitionBindings.Include(
            rdb => rdb.BasePermissions,
            rdb => rdb.RoleTypeKind
        )
    );
}

查询被重写如下:

using (ClientContext context = new ClientContext(weburl))
{
    var subwebs = context.Web.GetSubwebsForCurrentUser(new SubwebQuery());
    context.Load(subwebs,
        webs => webs.Include(
            subwebs.GetSecurityExpression(),
            web => web.Title,
            web => web.ServerRelativeUrl));
    context.ExecuteQuery();
}

这个编译得很好,但是在运行时它抛出一个Microsoft.SharePoint.Client.InvalidQueryExpressionException:

查询表达式"价值(Microsoft.SharePoint.Client.WebCollection) .GetSecurityExpression()"不支持

如果我正确理解异常,编译器不是调用subwebs.GetSecurityExpression(),而是从它创建一个表达式并将其传递给Include

我找到的解决方法是像这样编写调用代码:

using (ClientContext context = new ClientContext(weburl))
{
    var subwebs = context.Web.GetSubwebsForCurrentUser(new SubwebQuery());
    var securityExpression = subwebs.GetSecurityExpression();
    context.Load(subwebs,
        webs => webs.Include(
            securityExpression,
            web => web.Title,
            web => web.ServerRelativeUrl));
    context.ExecuteQuery();
}

这可以工作,但不像我希望的那样简洁。有没有办法让第一种方法起作用?

如何获取返回要调用的表达式而不是解释为表达式的函数

你面临的问题是,你的方法GetSecurityExpression将成为第一个变体表达式树的一部分,而SharePoint无法解析这一点。在第二种变体中,在创建表达式树之前对方法调用进行评估,并且实际的表达式树将包含正确的子树(即在GetSecurityExpression方法中编写的内容)。

原则上可以在执行查询之前对表达式树的部分求值。看看ReLinq;它有一个类partialevaluingexpressiontreevisitor来完成这项工作。但是,这将以较小的性能损失为代价。