首页 文章

LINQ表达式中的String.IsNullOrWhiteSpace

提问于
浏览
119

我有以下代码:

return this.ObjectContext.BranchCostDetails.Where(
    b => b.TarrifId == tariffId && b.Diameter == diameter
        || (b.TarrifId==tariffId && !string.IsNullOrWhiteSpace(b.Diameter))
        || (!b.TarrifId.HasValue) && b.Diameter==diameter);

当我尝试运行代码时出现此错误:

LINQ to Entities无法识别方法'Boolean IsNullOrWhiteSpace(System.String)'方法,并且此方法无法转换为商店表达式 . “

我怎样才能解决这个问题并编写比这更好的代码?

4 回答

  • 227

    你需要更换

    !string.IsNullOrWhiteSpace(b.Diameter)
    

    !(b.Diameter == null || b.Diameter.Trim() == string.Empty)
    

    对于Linq to Entities,这被转换为:

    DECLARE @p0 VarChar(1000) = ''
    ...
    WHERE NOT (([t0].[Diameter] IS NULL) OR (LTRIM(RTRIM([t0].[Diameter])) = @p0))
    

    而对于Linq到SQL差不多但不完全相同

    DECLARE @p0 NVarChar(1000) = ''
    ...
    WHERE NOT (LTRIM(RTRIM([t0].[TypeName])) = @p0)
    
  • 4

    在这种情况下,区分 IQueryable<T>IEnumerable<T> 很重要 . 简而言之, IQueryable<T> 由LINQ提供程序处理以提供优化的查询 . 在此转换期间,并非所有C#语句都受支持,因为它们无法将它们转换为后端特定查询(例如SQL),或者因为实现者没有预见到需要该语句 .

    相反, IEnumerable<T> 针对具体对象执行,因此不会被转换 . 因此,很常见的是, IEnumerable<T> 可以使用的构造不能与 IQueryable<T> 一起使用,并且由不同LINQ提供程序支持的 IQueryables<T> 不支持同一组函数 .

    但是,有一些解决方法(如Phil's answer)可以修改查询 . 此外,作为一种更通用的方法,可以在继续查询规范之前回退到 IEnumerable<T> . 但是,这可能会影响性能 - 特别是在限制时使用它(例如where子句) . 相比之下,在处理转换时,性能损失要小得多,有时甚至不存在 - 取决于您的查询 .

    所以上面的代码也可以像这样重写:

    return this.ObjectContext.BranchCostDetails
        .AsEnumerable()
        .Where(
            b => b.TarrifId == tariffId && b.Diameter == diameter
            || (b.TarrifId==tariffId && !string.IsNullOrWhiteSpace(b.Diameter))
            ||(!b.TarrifId.HasValue) && b.Diameter==diameter
        );
    

    NOTE: 该代码将比Phil's answer具有更高的性能影响 . 但是,它显示了原理 .

  • 7

    使用表达式visitor来检测对string.IsNullOrWhiteSpace的引用,并将它们分解为更简单的表达式 (x == null || x.Trim() == string.Empty) .

    以下是扩展访问者和使用它的扩展方法 . 它不需要使用特殊配置,只需调用WhereEx而不是Where .

    public class QueryVisitor: ExpressionVisitor
    {
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node.Method.IsStatic && node.Method.Name == "IsNullOrWhiteSpace" && node.Method.DeclaringType.IsAssignableFrom(typeof(string)))
            {
                //!(b.Diameter == null || b.Diameter.Trim() == string.Empty)
                var arg = node.Arguments[0];
                var argTrim = Expression.Call(arg, typeof (string).GetMethod("Trim", Type.EmptyTypes));
    
                var exp = Expression.MakeBinary(ExpressionType.Or,
                        Expression.MakeBinary(ExpressionType.Equal, arg, Expression.Constant(null, arg.Type)),
                        Expression.MakeBinary(ExpressionType.Equal, argTrim, Expression.Constant(string.Empty, arg.Type))
                    );
    
                return exp;
            }
    
            return base.VisitMethodCall(node);
        }
    }
    
    public static class EfQueryableExtensions
    {
        public static IQueryable<T> WhereEx<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> where)
        {
            var visitor = new QueryVisitor();
            return queryable.Where(visitor.VisitAndConvert(where, "WhereEx"));
        }
    }
    

    因此,如果您运行 myqueryable.WhereEx(c=> !c.Name.IsNullOrWhiteSpace()) ,它将被转换为 !(c.Name == null || x.Trim() == "") ,然后传递给任何(linq到sql / entities)并转换为sql .

  • 15

    您也可以使用它来检查空格:

    !String.IsNullOrEmpty(b.Diameter.Trim())
    

相关问题