3列

时间:2016-08-03 18:25:52

标签: c# entity-framework linq

我有一个包含许多字段的对象,但我想在3列(boolean [IsValid],int [Count]和date [CreateDate])上排序,有条件地依赖于布尔值。

数据:

|| ID || IsValid || Count || CreateDate  ||
==========================================
|| 1  ||  True   || 3     || 2016-05-01 ||
|| 2  ||  True   || 2     || 2016-07-12 ||
|| 3  ||  False  || NULL  || 2015-06-16 ||
|| 4  ||  False  || 1     || 2015-01-01 ||

我想要的订单是: 1)首先是有效的项目 2)如果有效,则按Count排序,否则按CreateDate排序(基本上为(o.IsValid) ? o.Count : o.CreateDate,但不起作用),以便订单

|| ID || IsValid || Count || CreateDate  ||
==========================================
|| 2  ||  True   || 2     || 2016-07-12 ||
|| 1  ||  True   || 3     || 2016-05-01 ||
|| 4  ||  False  || 1     || 2015-01-01 ||
|| 3  ||  False  || NULL  || 2015-06-16 ||

如果某些内容曾经有效,则可能会再次失效,但不会重置该计数。

我尝试过这样的事情:

db.OrderBy(o => new { o.IsValid, o.Count }).ThenBy(o => new { o.IsValid, o.CreateDate })

db.OrderByDescending(o => o.IsValid).ThenBy(o => new { o.SortOrder, o.CreateDate })

和其他组合,但我不知道如何让订单按预期工作。

任何人都可以提供帮助吗?

3 个答案:

答案 0 :(得分:4)

由于LINQ查询是经过延迟评估的,因此您可以通过几个连接在一起的不同查询来实现此目的。我不确定这对你有多高效,但它可以实现你的目标。

css()

或者如果您更喜欢LINQ方法语法,这应该可以。

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<OrderObject> db = new List<OrderObject> 
        { 
            new OrderObject { Count=1, CreateDate=DateTime.Now.Subtract(TimeSpan.FromDays(0)), IsValid=true },
            new OrderObject { Count=2, CreateDate=DateTime.Now.Subtract(TimeSpan.FromDays(1)), IsValid=false },
            new OrderObject { Count=3, CreateDate=DateTime.Now.Subtract(TimeSpan.FromDays(2)), IsValid=false },
            new OrderObject { Count=4, CreateDate=DateTime.Now.Subtract(TimeSpan.FromDays(3)), IsValid=true },
            new OrderObject { Count=5, CreateDate=DateTime.Now.Subtract(TimeSpan.FromDays(4)), IsValid=false },
        };

        var validItemsOrderedByCount = (from obj in db
                                        where obj.IsValid
                                        orderby obj.Count
                                        select obj);

        var nonValidItemsOrderedByDateCreated = (from obj in db
                                                 where obj.IsValid == false
                                                 orderby obj.CreateDate
                                                 select obj);

        var combinedList = validItemsOrderedByCount
            .Concat(nonValidItemsOrderedByDateCreated)
            .ToList();
    }
}

class OrderObject
{
    public bool IsValid { get; set; }
    public DateTime CreateDate { get; set; }
    public int Count { get; set; }
}

或者根据请求使用 var validItemsMethodSyntax = db.Where(x => x.IsValid).OrderBy(x => x.Count); var nonValidItemsMethodSyntax = db.Where(x => x.IsValid == false).OrderBy(x => x.CreateDate); var combinedMethodSyntax = validItemsMethodSyntax .Concat(nonValidItemsMethodSyntax) .ToList(); 和方法语法和一个变量。

Union

答案 1 :(得分:2)

我能说的最简单的是:

db.OrderByDescending(o => o.IsValid)
  .ThenBy(o => o.IsValid ? DbFunctions.AddSeconds(DateTime.MinValue, o.Count) : o.CreateDate)

我们的想法是将列CreateDateCount转换为相同的类型,然后根据它的值进行排序。对于linq到对象,我将两者都转换为整数表示:

.ThenBy(o => o.IsValid ? o.Count : o.CreateDate.Ticks)

但这对EF不起作用,因为它无法将.Ticks映射到sql查询。此外,我没有找到优雅的方式将datetime表示为整数值,因此,另一种方法是将现有的整数值转换为datetime。现在我们将两列都表示为datetime,并且可以根据它的值轻松排序。

更新

刚做了一些测试,发现DbFunctions.AddSeconds(DateTime.MinValue, o.Count)表示为datetime2,可能比1753年1月1日(datetime的最低值)更小。此表达式的值落在[01.01.01 00:00:00 .. 19.01.0069 3:14:07]范围内,但受.Count >= 0限制,因为负值会产生溢出错误。

让我们支持.Count的负值:

DbFunctions.AddSeconds(new DateTime(70, 1, 1), o.Count)

此表达式的值落在[13.12.0001 20:45:53 .. 20.01.0138 3:14:07]范围内,这绝对小于.CreateDate值(我猜,.CreateDate的最小值是当前或上个世纪,即{ {1}}比.CreateDate大得多,直到你在古代写软件为止。这就是为什么我们可以扔掉20.01.138

您问题的最终答案是单.OrderByDescending(o => o.IsValid)

order by

有效值将首先出现,因为它们将具有较小的db.OrderBy(o => o.IsValid ? DbFunctions.AddSeconds(new DateTime(70, 1, 1), o.Count) : o.CreateDate) 值,然后是无效的值。有效值将按升序datetime2排序。

但是,您可以轻松管理订单方向以及谁先行,有效或无效。例如,将.Count的基准日期更改为.Count而不是7000,将70乘以.Count,依此类推。

答案 2 :(得分:1)

您可以使用三元运算符执行此操作,如评论中提到的juharr:

db.ToList()
    .OrderBy(e => e.IsValid)
    .ThenBy(e => e.IsValid ? e.Count : 0)
    .ThenBy(e => e.IsValid ? DateTime.MinValue : e.CreateDate);

请记住,如果您正在进行任何过滤,请在.ToList()之前执行此过滤,以最大限度地减少从数据层返回的结果。