【问题标题】:In LINQ to Entities, How can you order like follows?在 LINQ to Entities 中,您如何按如下方式订购?
【发布时间】:2018-02-12 06:51:20
【问题描述】:

我有一个可查询的带有“代码”字段的实体

1.1.1,
1.1.2,
1.1.3,
1.1.4,
...,
1.1.10,
1.1.11

不幸的是,当我对查询执行.OrderBy(x=> x.Code) 时,它按以下顺序返回

1.1.1,
1.1.10,
1.1.11,
1.1.2,
1.1.3,
...

如何使对象列表按代码字段排序,由“.”分隔并在每个部分之间作为整数?

这是客户数据,所以我不能只在 1 个数字前面加上“0”。 在此代码字段中,它也是任意数量的“.”。

如果您需要更多信息,请告诉我。

【问题讨论】:

  • 这是什么数据类型?字符串?
  • 它是一个对象,但是Code字段是一个字符串
  • 这是逗号的一部分吗?或者是为了好玩
  • 我会创建 sql 函数,它将其转换为可排序的表示形式(例如,如果您使用 SQL Server,则转换为 hierarchyid 类型),然后使用该函数进行排序 (ctx.Codes.OrderBy(c => Db.MySortFunction(c.Code)))
  • 您可以创建第二个字段,该字段返回一个更易于排序的值。对于“X.Y.Z”的“代码”值,您可以返回“X*10^6+Y*10^3+Z”

标签: c# sql entity-framework linq


【解决方案1】:

如果您可以做出一些假设,例如每个节点最多可以有 n 个字母。您可以使用此代码。

.OrderBy(x => String.Concat( x.Code.Split('.')
                                   .Select(ss => ss.PadLeft(3, '0'))) )

【讨论】:

  • 谢谢!!!!我已经为此发疯了好多年了。这是解决我的问题的最干净的解决方案。我欠你的债!
  • 不客气!我很高兴听到它解决了您的问题。
【解决方案2】:

Version 类应该是一个很好的方法。不幸的是,Version.Parse 在 Linq2Entity 上失败了,所以你必须先从你的 sql-server 中获取数据,然后再对其进行排序。

var result = input.AsEnumerable<string>().OrderBy(x => Version.Parse(x.Code));

【讨论】:

  • 这就是答案。干得好。
  • "也可以是任意数量的"."在这个Code字段中",并且Version正好支持3个。
  • 是的,最多 3 个,但这仍然不是“任何数字”。所以这是“部分”解决方案,这意味着它根本不是解决方案。
【解决方案3】:

如果您的表中有大量数据 - 您不能真正使用需要从服务器提取所有数据然后在客户端排序的建议解决方案。要在服务器上执行此操作并且仍然使用实体框架,您需要创建 SQL 函数或可计算列并按其排序,除非您可以弄清楚如何仅使用实体框架可转换为 SQL 的函数将代码转换为可排序形式。如果您使用的是 SQL Server,则 SQL 函数示例如下:

CREATE FUNCTION dbo.DotSeparatedSort(@input varchar(max))
RETURNS hierarchyid
AS
BEGIN
    return CAST(N'/' + replace(@input, '.', '/') + N'/' AS hierarchyid)
END

这会将点分隔的数字转换为具有您需要的排序语义的hierarchyid 类型。

计算列也可以这样做(只需将CAST(N'/' + replace(Code, '.', '/') + N'/' AS hierarchyid) 作为公式)。

然后您需要将函数导入 EF 模型(详细信息取决于您使用的 EF 版本以及代码优先还是数据库优先)并按此排序:

ctx.Entities.OrderBy(c => ctx.DotSeparatedSort(c.Code))

这将被翻译成select * from Entities order by dbo.DotSeparatedSort(Code)。如果是计算列,您显然只是按此排序。

如果您使用计算列 - 您可以在其上定义索引,因此如果您有大量数据 - 因此我更喜欢计算列。

【讨论】:

    【解决方案4】:

    在作为查询(例如LINQ to Entities)执行时应用正确的排序(OrderBy)是有问题的,因为表达式直接转换为ORDER BY SQL 表达式,并且没有这样的SQL 子句。

    您可以做的是在不进行特定排序的情况下执行 SQL 以具体化数据(例如在其上运行 .ToList()),然后使用 this specific Sort Override 对内存中的数据进行排序。

    这就是您的自定义 Comparer 实现的样子(从上面的页面复制并更新,保留 cmets 原样等):

    public class CodeComparer : IComparer<MyEntity>
    {
        public int Compare(MyEntity x, MyEntity y)
        {
            if (x == null)
            {
                if (y == null)
                {
                    // If x is null and y is null, they're
                    // equal. 
                    return 0;
                }
                else
                {
                    // If x is null and y is not null, y
                    // is greater. 
                    return -1;
                }
            }
            else
            {
                // If x is not null...
                //
                if (y == null)
                    // ...and y is null, x is greater.
                {
                    return 1;
                }
                else
                {
                    return ToComparableString(x.Code).CompareTo(ToComparableString(y.Code));
    
                }
            }
        }
    
        private string ToComparableString(string input)
        {
            var split = input.Split(new [] {'.'});
            return string.Join(".", split.Select(x => x.PadLeft(5, '0')));
        }
    }
    

    然后,您应用排序:

    var query = (...your Query or data source...).ToList();
    var sortedList = query.Sort(new CodeComparer());
    

    它将像“1.15.141”这样的字符串转换为“00001.00015.0141”,使其具有可比性。 (您也可以删除点.,也可以将填充长度调整为预期的最大数字,例如,如果每个“节点”最多有 99 个,则将其设为 2 而不是 5。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多