다음 코드가 있습니다.
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.Diameter
is 인 경우 예외 가 발생합니다 null
.
여전히 진술을 사용하려면이 검사를 사용하는 것이 좋습니다
!String.IsNullOrWhiteSpace(b.Diameter), IsNullOrWhiteSpace = IsNullOrEmpty + WhiteSpace
답변
