C# LINQ 表达式中的 String.IsNullOrWhiteSpace
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9606979/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
String.IsNullOrWhiteSpace in LINQ Expression
提问by Hossein Moradinia
I have the following code:
我有以下代码:
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);
And I get this error when I try to run the code:
当我尝试运行代码时出现此错误:
LINQ to Entities does not recognize the method 'Boolean IsNullOrWhiteSpace(System.String)' method, and this method cannot be translated into a store expression."
LINQ to Entities 无法识别方法 'Boolean IsNullOrWhiteSpace(System.String)' 方法,并且此方法无法转换为存储表达式。”
How can I solve this problem and write code better than this?
我怎样才能解决这个问题并写出比这更好的代码?
采纳答案by Phil
You need to replace
你需要更换
!string.IsNullOrWhiteSpace(b.Diameter)
with
和
!(b.Diameter == null || b.Diameter.Trim() == string.Empty)
For Linq to Entities this gets translated into:
对于 Linq to Entities,这被翻译成:
DECLARE @p0 VarChar(1000) = ''
...
WHERE NOT (([t0].[Diameter] IS NULL) OR (LTRIM(RTRIM([t0].[Diameter])) = @p0))
and for Linq to SQL almost but not quite the same
对于 Linq to SQL 几乎但不完全相同
DECLARE @p0 NVarChar(1000) = ''
...
WHERE NOT (LTRIM(RTRIM([t0].[TypeName])) = @p0)
回答by AxelEckenberger
In this case it is important to distinguish between IQueryable<T>and IEnumerable<T>. In short IQueryable<T>is processed by a LINQ provider to deliver an optimized query. During this transformation not all C# statements are supported, as it either is not possible to translate them to a back-end specific query (e.g. SQL) or because the implementer did not foresee the need for the statement.
在这种情况下,区分IQueryable<T>和很重要IEnumerable<T>。简而言之IQueryable<T>,由 LINQ 提供程序处理以提供优化的查询。在此转换期间,并非所有 C# 语句都受支持,因为无法将它们转换为后端特定查询(例如 SQL),或者因为实施者没有预见到需要该语句。
In contrast IEnumerable<T>is executed against the concrete objects and, therefore, will not be transformed. So, it is quite common that constructs, which are useable with IEnumerable<T>, cannot be used with IQueryable<T>and also that IQueryables<T>backed by different LINQ providers do not support the same set of functions.
相反,它IEnumerable<T>是针对具体对象执行的,因此不会被转换。因此,IEnumerable<T>可与 一起使用、不能与 一起使用的构造IQueryable<T>以及IQueryables<T>由不同 LINQ 提供程序支持的构造不支持相同的函数集是很常见的。
However, there are some workarounds (like Phil's answer), which modify the query. Also, as a more general approach it is possible to drop back to an IEnumerable<T>before continuing with the specification of the query. This, however, might have a performance hit - especially when using it on restrictions (e.g. where clauses). In contrast, when dealing with transformations the performance hit is a lot smaller, sometimes even non existent - depending on your query.
但是,有一些变通方法(如Phil 的回答)可以修改查询。此外,作为一种更通用的方法,可以IEnumerable<T>在继续查询规范之前回退到。然而,这可能会影响性能 - 特别是在限制(例如 where 子句)上使用它时。相比之下,在处理转换时,性能影响要小得多,有时甚至不存在 - 取决于您的查询。
So the above code could also be rewritten like this:
所以上面的代码也可以改写成这样:
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:Ths code will have an higher performance impact than Phil's answer. However, it shows the principle.
注意:此代码将比Phil 的答案具有更高的性能影响。但是,它显示了原理。
回答by Sam
Use an expression visitor to detect references to string.IsNullOrWhiteSpace and break them down into a simpler expression (x == null || x.Trim() == string.Empty).
使用表达式访问者检测对 string.IsNullOrWhiteSpace 的引用并将它们分解为更简单的表达式(x == null || x.Trim() == string.Empty)。
So below is an extended visitor and an extension method to make use of it. It requires no special config to use, simply call WhereEx instead of Where.
所以下面是一个扩展的访问者和一个使用它的扩展方法。它不需要特殊的配置来使用,只需调用 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"));
}
}
So if you run myqueryable.WhereEx(c=> !c.Name.IsNullOrWhiteSpace())it will be converted to !(c.Name == null || x.Trim() == "")before being passes to whatever (linq to sql/entities) and converted to sql.
因此,如果您运行myqueryable.WhereEx(c=> !c.Name.IsNullOrWhiteSpace())它,它将!(c.Name == null || x.Trim() == "")在传递给任何东西(linq to sql/entities)并转换为 sql 之前转换为。
回答by Majid
You can also use this to check for whitespace:
您还可以使用它来检查空格:
b.Diameter!=null && !String.IsNullOrEmpty(b.Diameter.Trim())
回答by Duy Tran
!String.IsNullOrEmpty(b.Diameter.Trim())
will throw exception if b.Diameteris null.
If you still want to use your statement, better use this check
如果b.Diameter是,将抛出异常null。
如果您仍然想使用您的语句,最好使用此检查
!String.IsNullOrWhiteSpace(b.Diameter), IsNullOrWhiteSpace = IsNullOrEmpty + WhiteSpace

