LINQ 表达式中的 String.IsNullOrWhiteSpace

我有以下密码:

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);

当我尝试运行代码时,我得到了这个错误:

LINQtoEntity 不识别方法‘ Boolean’ IsNullOrWhiteSpace (System.String)的方法,此方法不能是 翻译成商店的表达方式”

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

44406 次浏览

你需要更换

!string.IsNullOrWhiteSpace(b.Diameter)

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

对于 Linq to Entity,这个转换为:

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<T>IEnumerable<T>很重要。简而言之,IQueryable<T>由 LINQ 提供程序处理以交付优化的查询。在这个转换过程中,并不支持所有的 C # 语句,因为要么不可能将它们转换为后端特定的查询(例如 SQL) ,要么因为实现者没有预见到对该语句的需求。

相反,IEnumerable<T>是针对具体对象执行的,因此不会进行转换。因此,可用于 IEnumerable<T>的构造常常不能用于 IQueryable<T>,而且由不同的 LINQ 提供程序支持的 IQueryables<T>不支持同一组函数。

但是,有一些变通方法(如 菲尔的回答)可以修改查询。另外,作为一种更一般的方法,在继续查询规范之前可以回退到 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
);

注意: 这段代码将比 菲尔的回答具有更高的性能影响。

使用表达式访问器检测对 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/实体)并转换为 sql。

你也可以用这个来检查空格:

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

如果 b.Diameternull将抛出异常。
如果你仍然想使用你的陈述,最好使用这个检查

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