我正在尝试将一些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<B> BList { get; set; }
}
我有一个我要查询的List<A>
对象。它是从数据库填充的,因此列表中的每个A都有一个负载为Bs的子列表。我想使用Linq-to-objects来查询这个列表。
所以我需要使用Linq来获得A开始和孩子Bs完成之间的平均时间,以及每个B开始和完成之间的平均时间。我没有写原始的SQL,所以我不完全确定它能做到它应该做到的!
我有几个这些平均值要计算,所以我想在一个神奇的Linq查询中完成所有这些。这可能吗?
答案 0 :(得分:5)
更有趣的问题是如何在服务器上执行此类操作,例如,将以下查询转换为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 => (x.B.Completed - x.A.Created).TotalSeconds);
var average2 = xs.Average(x => (x.B.Completed - x.B.Created).TotalSeconds);
我明白了吗?
编辑:大卫B回答同样的问题。我忘了将TimeSpan转换为Average方法支持的简单数字类型。使用TotalMilliseconds / Seconds / Minutes或任何适当的比例。
答案 1 :(得分:4)
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 x.i into g
select new
{
Avg1 = g.Average(x => (x.b.CompletedDate - x.a.CreatedDate)
.TotalMilliseconds ),
Avg2 = g.Average(x => (x.b.CompletedDate - x.b.CreatedDate)
.TotalMilliseconds )
};
答案 2 :(得分:1)
让我们从获得B开始和结束之间的平均时间开始:
1)首先需要每个B对象的开始和结束之间的时间差,所以你会这样做:
List<TimeSpan> timeSpans = new List<TimeSpan>();
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<TimeSpan> timeSpans_2 = new List<TimeSpan>();
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对象列表调用它们。