почему LINQ to SQL обманут методом расширения? что теперь?

StackOverflow https://stackoverflow.com/questions/609596

Вопрос

Рассмотрим мой класс Event и то, что я храню DateTime в БД как даты UTC. Я просто хочу вернуть отфильтрованный диапазон, основанный на текущей дате в определенном часовом поясе - не так ли?

Это отлично работает:

IQueryable<Event> test1 = this.GetSortedEvents().Where(e => e.FinishDateTime.Date >= DateTime.UtcNow.Date);

Это также отлично работает:

IQueryable<Event> test2 = this.GetSortedEvents().Where(e => e.FinishDateTime.AddHours(3).Date >= DateTime.UtcNow.AddHours(3).Date);

.. и дополнительно соответствует моим требованиям к часовому поясу.

Итак, я думаю, что могу перенести это конкретное преобразование в этот метод расширения:

    public static DateTime RiyadhTimeFromUtc(this DateTime utcTime)
    {
        return utcTime.AddHours(3);
    }

Это НЕ работает:

IQueryable<Event> test3 = this.GetSortedEvents().Where(e => e.FinishDateTime.RiyadhTimeFromUtc().Date >= DateTime.UtcNow.RiyadhTimeFromUtc().Date);

.. и я получаю это NotSupportedException: у метода 'System.DateTime RiyadhTimeFromUtc (System.DateTime)' нет поддерживаемого перевода в SQL.

Это явно мусор, поскольку компилятор успешно преобразовал его в SQL, когда идентичный код НЕ был в методе расширения.

Я столкнулся с " не поддерживает перевод на SQL " проблема раньше с определенными типами и совсем недавно DateTime's. Но мои тесты выше и эта ссылка доказывают, что метод AddHours должен быть поддерживается в переводе SQL.

Если бы кто-то мог сказать мне, что я делаю здесь неправильно (или другой подход к этой проблеме), я был бы очень благодарен.

Это было полезно?

Решение

Вы должны думать об этом в терминах дерева выражений. Именно так Linq-to-SQL анализирует ваш запрос, чтобы превратить его в SQL.

При проверке дерева он увидит объект DateTime и затем проверит, является ли вызываемый метод одним из поддерживаемых ( Add , AddHours и т. д.) поэтому, когда вы используете метод напрямую, он работает нормально.

Когда вы используете какой-либо другой метод расширения, он не может заглянуть внутрь этого метода, чтобы увидеть, что он делает, поскольку информация о теле этого метода отсутствует в дереве выражений, она скрыта в IL. Поэтому не имеет значения, поддерживается ли содержимое метода расширения, потому что Linq-to-SQL не может определить, что это за содержимое.

Смысл создания методов заключается в инкапсуляции и сокрытии информации, что обычно хорошо работает при разработке приложений, но, к сожалению, здесь скрывается информация от Linq-to-SQL, которую вам нужно видеть, чтобы ее можно было увидеть.

<Ч>

В ответ на отредактированный вопрос - как вы это решаете? Если вы хотите сохранить вычисление даты в выражении Linq, единственное, что вы можете сделать для обеспечения эффективности запроса, - это не использовать метод расширения, а просто использовать AddHours (3) непосредственно в < code> DateTime объект.

Это одно из печальных ограничений Linq. Подобно многим вещам, это несколько утечка абстракции, которая, хотя и обеспечивает общий синтаксис для ряда источников, имеет различные ограничения и ограничения в отношении того, какие операции может / будет поддерживать поставщик источника (например, это будет прекрасно работать в Linq-to -Объекты, так как для их выполнения не нужно переводить дерево выражений).

Другие советы

Похоже, что для LINQ-to-SQL можно выяснить, что RiyadhTimeFromUtc на самом деле был просто вызовом AddHours (3) , ему пришлось бы сделать несколько довольно сложный анализ вашего кода. Скажем, вы написали:

public static DateTime RiyadhTimeFromUtc(this DateTime utcTime)
{
    if (Random.GetInt() < 99) {
       return utcTime.AddHours(3);
    } else {
       return utcTime.AddDays(5);
    }
}

Как бы это перевести на SQL? Смотрите это обсуждение для получения дополнительной информации.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top