【问题标题】:How to get a function that returns an expression to be called rather than interpreted as an expression如何获取返回要调用的表达式而不是解释为表达式的函数
【发布时间】:2015-12-30 20:00:48
【问题描述】:

我正在使用 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();
}

这可行,但并不像我希望的那样简洁。有没有办法让第一种方法发挥作用?

【问题讨论】:

    标签: c# sharepoint lambda iqueryable


    【解决方案1】:

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

    原则上,可以在执行查询之前评估表达式树的某些部分。看看 ReLinq;它有一个类 PartialEvaluatingExpressionTreeVisitor 可以完成这项工作。但是,这会带来很小的性能损失。

    【讨论】:

      猜你喜欢
      • 2015-03-31
      • 2011-06-11
      • 2012-05-15
      • 1970-01-01
      • 2015-03-30
      • 1970-01-01
      • 2013-03-23
      • 2020-11-09
      • 1970-01-01
      相关资源
      最近更新 更多