LINQ表达式中的String.IsNullOrWhiteSpace
本文关键字:String IsNullOrWhiteSpace 表达式 LINQ | 更新日期: 2023-09-27 18:27:08
我有以下代码:
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无法识别方法"布尔"IsNullOrWhiteSpace(System.String)"方法,而此方法不能为翻译成商店表达式。"
我该如何解决这个问题并编写比这个更好的代码?
您需要更换
!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)
在这种情况下,区分IQueryable<T>
和IEnumerable<T>
很重要。简而言之,IQueryable<T>
由LINQ提供者处理,以传递优化的查询。在这个转换过程中,并不是所有的C#语句都得到支持,因为不可能将它们转换为后端特定的查询(例如SQL),或者因为实现者没有预见到对该语句的需求。
相反,IEnumerable<T>
是针对具体对象执行的,因此不会被转换。因此,很常见的情况是,可与IEnumerable<T>
一起使用的构造不能与IQueryable<T>
一起使用,而且由不同LINQ提供程序支持的IQueryables<T>
不支持同一组函数。
但是,也有一些变通方法(比如Phil的答案),可以修改查询。此外,作为一种更通用的方法,可以在继续指定查询之前返回到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
);
注意:与Phil的回答相比,这些代码对性能的影响更大。然而,它显示了原理。
使用表达式访问者来检测对字符串的引用。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())
如果CCD_ 12是CCD_
如果你仍然想使用你的声明,最好使用这个检查
!String.IsNullOrWhiteSpace(b.Diameter), IsNullOrWhiteSpace = IsNullOrEmpty + WhiteSpace