String.IsNullOrWhiteSpace в выражении LINQ - PullRequest
135 голосов
/ 07 марта 2012

У меня есть следующий код:

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) ', и этот метод не может быть переведено в магазинное выражение. "

Как я могу решить эту проблему и написать код лучше, чем этот?

Ответы [ 5 ]

243 голосов
/ 07 марта 2012

Вам нужно заменить

!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 to SQL почти, но не совсем то же самое

DECLARE @p0 NVarChar(1000) = ''
...
WHERE NOT (LTRIM(RTRIM([t0].[TypeName])) = @p0)
18 голосов
/ 24 января 2013

В этом случае важно различать IQueryable<T> и IEnumerable<T>.Вкратце IQueryable<T> обрабатывается поставщиком LINQ для доставки оптимизированного запроса.Во время этого преобразования поддерживаются не все операторы C #, так как либо невозможно перевести их в специфичный для внутреннего интерфейса запрос (например, SQL), либо потому, что разработчик не предвидел необходимость этого оператора.

В отличие от этого IEnumerable<T> выполняется против конкретных объектов и, следовательно, не будет трансформироваться.Таким образом, довольно часто конструкции, которые можно использовать с IEnumerable<T>, нельзя использовать с IQueryable<T>, а также что IQueryables<T>, поддерживаемые различными поставщиками LINQ, не поддерживают один и тот же набор функций.Однако есть некоторые обходные пути (например, ответ Фила ), которые изменяют запрос.Кроме того, в качестве более общего подхода можно вернуться к IEnumerable<T>, прежде чем продолжить спецификацию запроса.Это, однако, может привести к снижению производительности, особенно при использовании его в ограничениях (например, в предложениях where).Напротив, при работе с преобразованиями снижение производительности намного меньше, а иногда и вовсе отсутствует - в зависимости от вашего запроса.

Таким образом, приведенный выше код также можно переписать так:* ПРИМЕЧАНИЕ: Этот код окажет более сильное влияние на производительность, чем Ответ Фила .Тем не менее, это показывает принцип.

9 голосов
/ 02 декабря 2016

Использование посетителя выражения для обнаружения ссылок на строку. 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 to sql / entity) и преобразован в sql.

3 голосов
/ 18 октября 2017

Вы также можете использовать это для проверки пробелов:

!String.IsNullOrEmpty(b.Diameter.Trim())
0 голосов
/ 24 апреля 2019
!String.IsNullOrEmpty(b.Diameter.Trim()) 

сгенерирует исключение, если b.Diameter равно null.
Если вы все еще хотите использовать свое заявление, лучше используйте эту проверку

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