Using QueryOver projections and aggregates
使用QueryOver投影和聚合
在一些情况下,只需要某个实体的特定属性.而其他情况下,却可能需要一个聚合函数的结果,比如average或count.本节介绍QueryOver查询中的投影和聚合.
步骤
1. 完成本章简介中的通用步骤.
2. 在Queries类中,添加下面的方法:
public IEnumerable<NameAndPrice> GetMoviePriceList() { return _session.QueryOver<Movie>() .Select(m => m.Name, m => m.UnitPrice) .List<object[]>() .Select(props => new NameAndPrice() { Name = (string)props[0], Price = (decimal)props[1] }); }
3. 在Queries类中,添加下面的方法,该方法获取movies的价格平均值:
public decimal GetAverageMoviePrice() { var result = _session.QueryOver<Movie>() .Select(Projections.Avg<Movie>(m => m.UnitPrice)) .SingleOrDefault<double>(); return Convert.ToDecimal(result); }
4. 在Queries类中,添加下面的方法,该方法获取movies的价格平均值和directors列表:
public IEnumerable<NameAndPrice> GetAvgDirectorPrice() { return _session.QueryOver<Movie>() .SelectList(list => list .SelectGroup(m => m.Director) .SelectAvg(m => m.UnitPrice) ) .List<object[]>() .Select(props => new NameAndPrice() { Name = (string)props[0], Price = Convert.ToDecimal(props[1]) }); }
5. 在Program.cs中, 为RunQueries方法添加下述代码:
static void RunQueries(ISession session) { var queries = new Queries(session); Show("Movie Price List:", queries.GetMoviePriceList()); Show("Average Movie Price:", queries.GetAverageMoviePrice()); Show("Average Price by Director:", queries.GetAvgDirectorPrice()); }
6. 编译运行,结果如下图所示:
原理
让我们来逐一地来查看这些查询:
- GetMoviePriceList 查询
查询movie价格列表的代码如下:
_session.QueryOver<Movie>() .Select(m => m.Name, m => m.UnitPrice) .List<object[]>() .Select(props => new NameAndPrice() { Name = (string)props[0], Price = (decimal)props[1] });
在这个查询中,我们想要返回一个包含movie名字和价格的列表.为了达成该目的,我们使用了QueryOver的Select方法从Movie对象中抓取了两个属性Name和UnitPrice. 在QueryOver结尾调用了List.因为我们返回了特定的属性值,而不是整个Movie对象.泛型参数指定了将返回一个对象的数组list,该list中的每个元素代表查询结果集中的一行,每个对象数组的第一个元素都是movie的Name属性.第二个元素是movie的UnitPrice属性.
在Microsoft SQL Server中, 生成的SQL语句如下:
SELECT this_.Name as y0_, this_.UnitPrice as y1_ FROM Product this_ WHERE this_.ProductType = 'Eg.Core.Movie'
为了返回强类型的对象list,而不再返回这些对象的数组,我们使用了标准的LINQ to Objects:System.Linq中的Select来将查询结果放入整洁的NameAndPrice对象.
- GetAverageMoviePrice query
_session.QueryOver<Movie>() .Select(Projections.Avg<Movie>(m => m.UnitPrice)) .SingleOrDefault<double>();
上面的代码中,查询了数据库中所有movies的平均价格.调用了聚合函数Projections.Avg,然后投影出结果.
因为我们已经投影了一个聚合结果,我们可以调用.SingleOrDefault<double>()来执行查询和获得结果集.我们期望得到一个double类型的结果,然而由于我们处理的是货币,所以需要在将她返回给应用程序之前需要将其转换为decimal类型.
在Microsoft SQL Server中, 生成的SQL语句如下:
SELECT avg(cast(this_.UnitPrice as DOUBLE PRECISION)) as y0_ FROM Product this_ WHERE this_.ProductType = 'Eg.Core.Movie'
- GetAvgDirectorPrice 查询
使用下面的代码,查询了movies的directors列表和平均价格:
_session.QueryOver<Movie>() .Select(list => list .SelectGroup(m => m.Director) .SelectAvg(m => m.UnitPrice) ) .List<object[]>() .Select(props => new NameAndPrice() { Name = (string)props[0], Price = Convert.ToDecimal(props[1]) });
这样的话,我们将投影Director属性和UnitPrice属性的平均值,并依据Director属性对结果集分组,代码如下:
.SelectList(list => list .SelectGroup(m => m.Director) .SelectAvg(m => m.UnitPrice) )
如同在第一个查询中所做的一样,我们返回了一个对象数组list,然后将使用LINQ to Objects将其转换为一个NameAndPrice对象list.
在Microsoft SQL Server中, 生成的SQL语句如下:
SELECT this_.Director as y0_, avg(cast(this_.UnitPrice as DOUBLE PRECISION)) as y1_ FROM Product this_ WHERE this_.ProductType = 'Eg.Core.Movie' GROUP BY this_.Director