C# LINQ表达式中的String.IsNullOrWhiteSpace

C# LINQ表达式中的String.IsNullOrWhiteSpace,c#,linq,lambda,null,linq-to-entities,C#,Linq,Lambda,Null,Linq To Entities,我有以下代码: return this.ObjectContext.BranchCostDetails.Where( b => b.TarrifId == tariffId && b.Diameter == diameter || (b.TarrifId==tariffId && !string.IsNullOrWhiteSpace(b.Diameter)) || (!b.TarrifId.HasValue) &

我有以下代码:

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实体无法识别方法“布尔” IsNullOrWhiteSpace(System.String)“”方法,并且不能使用此方法 翻译成商店表达式。”

如何解决此问题并编写比此更好的代码?

您需要替换

!string.IsNullOrWhiteSpace(b.Diameter)

对于Linq to实体,这将转换为:

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)

在这种情况下,区分
IQueryable
IEnumerable
是很重要的。简言之,
IQueryable
由LINQ提供程序处理,以提供优化的查询。在转换过程中,并非所有C#语句都受支持,因为也不可能将它们转换为后端特定的查询(例如SQL)或因为实现者没有预见到需要该语句

相比之下,
IEnumerable
是针对具体对象执行的,因此不会被转换。因此,可以与
IEnumerable
一起使用的构造不能与
IQueryable
一起使用,并且由不同LINQ提供程序支持的
IQueryables
不支持ame函数集

但是,有一些变通方法(如)可以修改查询。此外,作为一种更通用的方法,可以在继续指定查询之前返回到
IEnumerable
。但是,这可能会影响性能,尤其是在限制条件(例如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
    );

注意:这段代码对性能的影响比。但是,它显示了原理。

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

下面是一个扩展的访问者和一个使用它的扩展方法。它不需要使用特殊的配置,只需调用wherex而不是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"));
    }
}
公共类查询访问者:ExpressionVisitor
{
受保护的重写表达式VisitMethodCall(MethodCallExpression节点)
{
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))
);
返回经验;
}
返回base.VisitMethodCall(节点);
}
}
公共静态类EfQueryableExtensions
{
公共静态IQueryable wherex(此IQueryable可查询,表达式where)
{
var visitor=newqueryvisitor();
返回queryable.Where(visitor.VisitAndConvert(Where,wherex));
}
}

因此,如果运行
myqueryable.wherex(c=>!c.Name.IsNullOrWhiteSpace())
它将被转换为
!(c.Name==null | | x.Trim()==“”)
,然后再被传递到任何对象(linq到sql/实体)并转换为sql。

您还可以使用它来检查空白:

b.Diameter!=null && !String.IsNullOrEmpty(b.Diameter.Trim())
如果
b.Diameter
null

如果你还想用你的对账单,最好用这张支票

!String.IsNullOrWhiteSpace(b.Diameter), IsNullOrWhiteSpace = IsNullOrEmpty + WhiteSpace

为什么?此代码编译:
List my=new List();var i=from m in my where!string.IsNullOrWhiteSpace(m)选择m;
它可以编译,但不会被Linq转换为SQL to entities.Method'Boolean IsNullOrWhiteSpace(System.string)“”没有支持的到SQL的转换。这同样适用于IsNullOrEmpty。对于Linq到SQLA也是如此。警告一句:在“”上使用“string.Empty”是非常重要的(也称为空字符串)。前者有效,后者无效(至少就Oracle的EF驱动程序而言)。如果使用:b.Diameter.Trim()==”“似乎Trim()也不受支持,至少对于使用MongoDB.DriverA的查询,对于这样一个简单的要求,它比Phil的答案复杂得多,但对于ExpressionVisitor的教育目的来说非常有趣,谢谢。如果diameter为空,这将引发一个异常。@OkanKocyigit您是对的。我已经编辑了答案。:)欢迎来到StackOverflow!首先,感谢您作为回答者参与SO。请看一看,以创建一个清晰易读的答案。
!String.IsNullOrEmpty(b.Diameter.Trim()) 
!String.IsNullOrWhiteSpace(b.Diameter), IsNullOrWhiteSpace = IsNullOrEmpty + WhiteSpace