我有一个包含许多字段的对象,但我想在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 })
和其他组合,但我不知道如何让订单按预期工作。
任何人都可以提供帮助吗?
答案 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)
我们的想法是将列CreateDate
和Count
转换为相同的类型,然后根据它的值进行排序。对于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()
之前执行此过滤,以最大限度地减少从数据层返回的结果。