使用 LINQ to Entities 进行自然排序

Natural sorting with LINQ to Entities

我尝试使用 LINQ to Entities 实现自然排序,使用等效于此 SQL 语句:

ORDER BY case when name like '[0-9]%' then 1 else 0 end, name

我的 LINQ 查询是:

query.OrderByDescending(a => a.Name.StartsWith("[0-9]") ? 1 : 0)

LINQ to Entities 虽然在模式中添加波浪号 (~)。

它正在生成这样的 SQL 查询:

SELECT CASE WHEN (Extent1.name LIKE '~[0-9]%'  escape '~') THEN 1 ELSE 0 END AS [C1], name
        from accounts extent1
         order by c1 asc

如何删除后面附加的波浪号 (~),例如 '~[0-9]%'?

生成的查询没有任何问题,它应该是这样。您的查询要求输入以确切字符串 [0-9] 开头的名称。

String.StartsWith(x) 是一个 string 方法,它检查字符串是否以文字开头,没有模式匹配。 Linq to Entities 翻译此 LIKE 'x%',其中 x 是文字字符串,而不是模式。 [ 是 LIKE 语句中的一个特殊字符。这意味着它必须使用 LIKE '~[0-9]%' escape '~' 进行转义。 LIKE 运算符允许您指定转义字符,在本例中为 ~.

我怀疑您不想要以 [0-9] 开头的名称,而是想要以数字开头的名称,即 LIKE '[0-9]%'。 String.StartsWith 不支持模式,也没有其他 String 方法支持。

一种解决方案是在查询中使用 SqlFunctions.PatIndex 并过滤 return 1 的行。不过我会检查执行计划,因为我怀疑查询会变慢。 LIKE '[0-9]%本质上是对所有从0开始到9之后的字母不包括的所有字符串的范围搜索,即A。这意味着服务器可以使用 Name 上的索引。使用 PATINDEX,它可能必须处理所有行。

不幸的是,SqlFunctions 不包含 Like 或任何类似的方法,这些方法会生成带有模式匹配的 LIKE 语句。

另一种选择是使用 a.Name >="0" && a.Name <"A" 实际请求范围查询。

更新 - 自然排序

这是XY Problem的一个案例。实际问题 X 是如何使用 LINQ to Entities 执行自然排序。自然排序的T-SQL解决方案之一是使用ORDER BY子句中的公式结合名称本身,使数字出现在纯文本之后,例如:

ORDER BY case when name like '[0-9]%' then 1 else 0 end, name

不幸的是,这不适用于 EF,因为没有等同于 LIKE 的模式。

可以使用 PATINDEX 执行相同的排序, 可通过 SqlFunctions.PatIndex 函数获得:

order by name, case when PATINDEX('[0-9]%',name)=1 then 1 else 0 end

等效的 LINQ 代码可以是:

query.OrderBy(a => {
                     SqlFunctions.PatIndex("[0-9]%",a.Name)==1? 1:0,
                     a.Name
                   })