[C#] 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);

코드를 실행하려고하면이 오류가 발생합니다.

LINQ to Entities는 ‘Boolean 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 공급자가 처리하여 최적화 된 쿼리를 제공합니다. 이 변환 중에 백엔드 특정 쿼리 (예 : 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의 답변 보다 성능에 더 큰 영향을 미칩니다 . 그러나 원칙을 보여줍니다.


답변

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 로 변환 되기 전에로 변환됩니다.


답변

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

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


답변

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

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

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


답변