Странное поведение в Entity Framework Linq в строке EndsWith метод

Фон

У меня есть таблица, содержащая только один столбец: Имя. В нем всего четыре строки, скажем

| Name      |
| test1.com |
| test2.com |
| test3.com |
| test4.com |

Проблема

Если я запрошу

var email = "[email protected]";
Table.Where(x => email.EndsWith(x.Name));

Я получу пустой список. но если я сначала запрашиваю все строки и вычисляю Where in memory like this

var email = "[email protected]";
Table.ToList().Where(x => email.EndsWith(x.Name));

Я получу список, содержащий только test2.com, который является правильным.

Сгенерированный SQL для первого запроса

SELECT "Extent1"."Name" AS "Name"
FROM "USER"."Table" "Extent1"
WHERE (( NVL(INSTR(REVERSE(:p__linq__0), REVERSE("Extent1"."Name")), 0) ) = 1)

Я попытался заменить: p__linq__0 на '[email protected]' и запустить запрос в SQLDeveloper, результат правильный.

Дополнительная информация

Если я изменил EndsWith() на Содержит(), проблема исчезнет. Вот сгенерированный SQL для Contains()

SELECT "Extent1"."Name" AS "Name"
FROM "USER"."Table" "Extent1"
WHERE (( NVL(INSTR(:p__linq__0, "Extent1"."Name"), 0) ) > 0)

Есть ли у вас какое-либо представление о том, что не так с методом EndsWith или REVERSE?

Окружающая среда

  • EF5.0
  • .NET4.5
  • Oracle11g
  • ODP.NET11.2 Release 3

Ответ 1

Эта строка относится ко мне и является общей ошибкой людей, использующих EF:

Table.ToList().Where(x => email.EndsWith(x.Name));

Часть Table.ToList() является наихудшей частью, потому что это фактически материализует целую таблицу в памяти, а затем выполняет EndsWith в С#. Эта строка:

Table.Where(x => email.EndsWith(x.Name));

Я бы предостерег этот подход только по общему принципу, поскольку он будет ужасно медленным, когда таблица вырастет до разумного размера. Вы можете сделать тяжелую работу до того, как запрос попадет в базу данных, разделив домен с электронной почтой при построении запроса:

var email = "[email protected]";

/* You should null check this of course and not just assume a match was found */
var domain = Regex.Match(email , "@(.*)").Groups[1].Value; 

/* Note: ToList() materialisation happens at the end */
var result = Table.Where(x => x.Name == domain).ToList();  

Кроме того, если вам нужно сопоставить имена доменов, хранящие электронные письма, то мой предпочтительный подход состоял бы в том, чтобы разделить электронное письмо и сохранить доменное имя в отдельном столбце, который вы индексируете и просто сопоставляете, это будет масштабироваться и быть намного проще в управлении. Помните, что данные в эти дни дешевы... особенно по сравнению с неиндексируемыми сканами таблиц.

Также помните (для обоих сценариев), что ваша база данных установлена ​​на CI (без учета регистра)