Linq – 在一个查询中计算多个平均值

我正在尝试将一些SQL查询转换为Linq,以避免多次访问数据库。

我正在尝试转换的旧SQL:

SELECT AVG(CAST(DATEDIFF(ms, A.CreatedDate, B.CompletedDate) AS decimal(15,4))), AVG(CAST(DATEDIFF(ms, B.CreatedDate, B.CompletedDate) AS decimal(15,4))) FROM dbo.A INNER JOIN dbo.B ON B.ParentId = A.Id 

所以我创建了两个C#类:

 class B { public Guid Id { get; set; } public DateTime CreatedDate { get; set; } public DateTime CompletedDate { get; set; } } class A { public Guid Id { get; set; } public DateTime CreatedDate { get; set; } public List BList { get; set; } } 

我有一个我要查询的List对象。 它是从数据库中填充的,因此列表中的每个A都有一个负载为Bs的子列表。 我想使用Linq-to-objects来查询此列表。

所以我需要使用Linq来获得A开始和孩子Bs完成之间的平均时间,以及每个B开始和完成之间的平均时间。 我没有写原始的SQL所以我不完全确定它做了它应该做的!

我有几个这些平均值要计算,所以我想在一个神奇的Linq查询中完成所有这些。 这可能吗?

更有趣的问题是如何在服务器上执行此类操作,例如,将以下查询转换为LINQ to SQL。

  var q = from single in Enumerable.Range(1, 1) let xs = sourceSequence select new { Aggregate1 = xs.Sum(), Aggregate2 = xs.Average(), // etc }; 

但是,如果您使用LINQ to Objects,那么尝试将其塞入一个查询中是没有意义的。 只需单独编写聚合:

 var aggregate1 = xs.Sum(); var aggregate2 = xs.Average(); // etc 

换一种说法:

 var xs = from a in listOfAs from b in a.Bs select new { A=a, B=b }; var average1 = xs.Average(x => (xBCompleted - xACreated).TotalSeconds); var average2 = xs.Average(x => (xBCompleted - xBCreated).TotalSeconds); 

我明白了吗?

编辑:大卫B回答同样的问题。 我忘了将TimeSpan转换为Average方法支持的简单数字类型。 使用TotalMilliseconds / Seconds / Minutes或任何适当的比例。

Sql和Linq在某些方面有所不同。 Sql具有隐式分组 – 将所有这些分组并创建一个组。 在linq中,可以通过引入常量来伪造隐式分组。

 var query = from i in Enumerable.Repeat(0, 1) from a in AList from b in a.BList select new {i, a, b} into x group x by xi into g select new { Avg1 = g.Average(x => (xbCompletedDate - xaCreatedDate) .TotalMilliseconds ), Avg2 = g.Average(x => (xbCompletedDate - xbCreatedDate) .TotalMilliseconds ) }; 

让我们从获得B开始和结束之间的平均时间开始:

1)首先需要每个B对象的开始和结束之间的时间差,所以你会这样做:

 List timeSpans = new List(); foreach (B myB in BList) { TimeSpan myTimeSpan = myB.CompletedDate.Subtract(myB.CreatedDate); timeSpans.Add(myTimeSpan); } 

2)你现在需要得到平均值。 您可以使用lambda表达式执行此操作:

 //This will give you the average time it took to complete a task in minutes Double averageTimeOfB = timeSpans.average(timeSpan => timeSpan.TotalMinutes); 

你现在可以做同样的事情来获得A开始和B完成之间的平均时间如下:

1)获取A的开始日期和B的完成日期的每个时间差:

  List timeSpans_2 = new List(); foreach (B myB in BList) { TimeSpan myTimeSpan = myB.CompletedDate.Subtract(objectA.CreatedDate); timeSpans_2.Add(myTimeSpan); } 

2)获得这些时间差的平均值与上面的完全相同:

 //This will give you the average time it took to complete a task in minutes Double averageTimeOfAandB = timeSpans_2.average(timeSpan => timeSpan.TotalMinutes); 

而且你已经完成了。 我希望这个对你有用。 请注意,此代码尚未经过测试。

编辑:请记住,LINQ使用Lamda表达式,但更多的是语法糖(不太了解实现,所以我希望你不要反对我)。 此外,您还可以将我在方法中提供的实现包装起来,以便您可以多次为A对象列表调用它们。