LINQ 식의 String.IsNullOrWhiteSpace


151

다음 코드가 있습니다.

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)'메소드를 인식하지 못하므로이 메소드를 상점 표현식으로 변환 할 수 없습니다. "

이 문제를 해결하고 이보다 더 나은 코드를 작성하려면 어떻게해야합니까?

답변:


263

교체해야합니다

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

3
왜? 이 코드는 다음과 같이 컴파일됩니다.List<string> my = new List<string>(); var i = from m in my where !string.IsNullOrWhiteSpace(m) select m;
Eric J.

37
컴파일 할 수는 있지만 Linq가 엔티티로 SQL로 변환하지는 않습니다. 'Boolean IsNullOrWhiteSpace (System.String)'메서드는 SQL 로의 변환을 지원하지 않습니다. IsNullOrEmpty에도 동일하게 적용됩니다.
Phil

1
Linq to SQL의 경우도 마찬가지입니다
Phil

3
주의 사항 : ""(빈 문자열)보다 'string.Empty'를 사용하는 것이 가장 중요합니다. 전자는 후자의 경우 (오라클의 EF 드라이버에 관한 한)는 그렇지 않습니다. 당신이 사용하는 경우 일명 : b.Diameter.Trim () == ""<-이것은 의도 한대로 작동하지 않습니다 (미쳤습니다 ...)
XDS

Trim ()도 MongoDB.Driver를 사용하는 쿼리에 대해 지원되지 않는 것 같습니다.
Leandro hereñu

20

이 경우 IQueryable<T>와 를 구분하는 것이 중요합니다 IEnumerable<T>. 간단히 말해서 IQueryable<T>LINQ 공급자가 처리하여 최적화 된 쿼리를 제공합니다. 이 변환 중에 백엔드 특정 쿼리 (예 : SQL)로 변환 할 수 없거나 구현자가 명령문의 필요성을 예측하지 않았기 때문에 모든 C # 명령문이 지원되는 것은 아닙니다.

대조적 IEnumerable<T>으로 콘크리트 오브젝트에 대해 실행되므로 변환되지 않습니다. 그래서,으로 가능한있는 구조는 것이 매우 일반적이다 IEnumerable<T>, 함께 사용할 수 없습니다 IQueryable<T>및 해당 IQueryables<T>기능의 동일한 집합을 지원하지 않는 다른 LINQ 공급자의 지원.

그러나 쿼리를 수정하는 몇 가지 해결 방법 (예 : 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의 답변 보다 성능에 더 큰 영향을 미칩니다 . 그러나 원칙을 보여줍니다.


10

expression 방문자를 사용하여 string.IsNullOrWhiteSpace에 대한 참조를 감지하고 더 간단한 표현식으로 분류하십시오 (x == null || x.Trim() == string.Empty).

아래는 확장 방문자와이를 이용하는 확장 방법입니다. 특별한 설정이 필요하지 않습니다. Where 대신 WhereEx를 호출하면됩니다.

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 to sql / entities) 전달되고 sql 로 변환 되기 전에로 변환됩니다.


이러한 간단한 요구 사항에 대한 Phil의 답변보다 훨씬 더 복잡하지만 ExpressionVisitor에 대한 교육 목적에 매우 흥미 롭습니다.
AFract

2

이것을 사용하여 공백을 확인할 수도 있습니다.

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

6
직경이 null 인 경우 예외가 발생합니다.
Okan Kocyigit

@OkanKocyigit 당신이 맞아요. 답변을 편집했습니다. :)
Majid

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

b.Diameteris 인 경우 예외 가 발생합니다 null.
여전히 진술을 사용하려면이 검사를 사용하는 것이 좋습니다

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

2
StackOverflow에 오신 것을 환영합니다! 우선, 답변자로서 SO에 참여해 주셔서 감사합니다. 명확하고 읽기 쉬운 답변을 만들기 위해 형식 을 살펴 보십시오.
Hille
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.