Entity Framework Linq 在字符串 EndsWith 方法中的奇怪行为

Weird behavior in Entity Framework Linq in string EndsWith method

背景

我有一个 table,它只包含一列:名称。 里面只有四行,比方说

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

问题

如果我查询

var email = "a@test2.com";
Table.Where(x => email.EndsWith(x.Name));

我会得到一个空列表。但是如果我先查询所有行并像这样计算内存中的位置

var email = "a@test2.com";
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)

我尝试用 'a@test2.com' 和 运行 替换 :p__linq__0 SQLDeveloper 中的查询,结果是正确的。

更多信息

如果我将 EndsWith() 更改为 Contains(),问题就会消失。这是为 Contains()

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

你知道 EndsWith 或 REVERSE 方法有什么问题吗?

环境

这条线让我很担心,这是使用 EF 的人常见的陷阱:

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

部分Table.ToList()是最糟糕的部分,因为这实际上会将整个table具体化到内存中,然后执行EndsWith C#。 这一行:

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

我只是根据一般原则警告这种方法,因为当 table 增长到合理的大小时,它会非常慢。您可以在查询访问数据库之前完成繁重的工作,方法是在构建查询时从电子邮件中分离出域:

var email = "a@test2.com";

/* 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();  

此外,如果您需要匹配存储电子邮件的列的域名,那么我的首选方法是拆分电子邮件并将域名存储在您索引并匹配的单独列中,这将扩展并且更容易管理。请记住,如今数据很便宜......尤其是与不可索引的 table 扫描相比。

还要记住(对于这两种情况)您的数据库设置为 CI(不区分大小写)