最近在开发一个内部的Report网站,外挂在POS系统上,因国内的POS系统基本都是无法自定义开发报表,源码已加密,无法客制化页面。而且厂商也不可能单独为你开发报表,每个公司都有自己的管理思维,所需的报表也不一样,当然除非公司的Money够多也愿意在IT投入,可以跟厂商谈谈定制化开发。
这几年基本都在折腾SAP,把ASP.Net忘得也差不多,重新捡起来,因报表数据复杂,需要将两个存储过程的结果合并为一个报表,然后按门店汇总,再按城市汇总,并且结果在GridViw中镶套三层显示。比较好的办法就是用Linq对结果进行处理,而这两个结果集数据不太一样,需要用Linq Left,结果按goole到的案例去Coding,报表抛出异常:未将对象引用设置到对象的实例,左看右看折腾了一晚上没找到原因,早上再来看代码找到原因了,在Left Join时,右边的结果集,可能出现空的结果,字段赋值时需对表进行null的判断,代码如下:
通过存储过程从DB中得到两个DataTable,dtHy和dtYz,使用Linq先对这两个表按城市、门店、店员汇总
//Linq按店员汇总dtHy
var SumHYByDY = from hy in dtHy.AsEnumerable()
group hy by new { city = hy.Field<string>("city"), mdmc = hy.Field<string>("mdmc"), dymc = hy.Field<string>("dymc") } into tb
select new
{
city = tb.Key.city,
mdmc = tb.Key.mdmc,
dymc = tb.Key.dymc,
hy_hys = tb.Sum(hy => Convert.ToInt32(hy["hys"] == DBNull.Value ? 0 : hy["hys"])),
hy_hys_hb = tb.Average(hy => Convert.ToDecimal(hy["hys_hb"] == DBNull.Value ? 0 : hy["hys_hb"]))
};
//Linq按店员汇总dtYz
var SumYZByDY = from yz in dtYz.AsEnumerable()
group yz by new { city = yz.Field<string>("city"), mdmc = yz.Field<string>("mdmc"), dymc = yz.Field<string>("dymc") } into tb
select new
{
city = tb.Key.city,
mdmc = tb.Key.mdmc,
dymc = tb.Key.dymc,
yz_hys = tb.Sum(yz => Convert.ToInt32(yz["hys"] == DBNull.Value ? 0 : yz["hys"])),
yz_hys_hb = tb.Average(yz => Convert.ToDecimal(yz["hys_hb"] == DBNull.Value ? 0 : yz["hys_hb"]))
};
注意Group by多个字段时,请使用new { }.
第二步,对Linq结果集再次Linq Left Join:
//Linq按城市、门店、店员合并数据 LeftJoin
var JoinByDymc = from hy in SumHyByDYMC
join yz in SumYzByDYMC
on new { city = hy.city, mdmc = hy.mdmc, dymc = hy.dymc }
equals new { city = yz.city, mdmc = yz.mdmc, dymc = yz.dymc }
into LeftJoinTB
from tb in LeftJoinTB.DefaultIfEmpty()
select new
{
hy.city,
hy.mdmc,
hy.dymc,
hy.hy_hys,
hy.hy_hys_hb,
yz_hys = tb == null ? 0 : tb.yz_hys,
yz_hys_hb = tb == null ? 0 : tb.yz_hys_hb
};
左连接时,需把左边的Table放在Join的左边,同时把结果into 到一个临时的结果集中,并且对该结果集使用方法DefaultIfEmpty(),即:
into LeftJoinTB from tb in LeftJoinTB.DefaultIfEmpty()
问题来了,如果将 yz_hys = tb == null ? 0 : tb.yz_hys 修改为yz_hys = tb.yz_hys,那么将抛出异常:未将对象引用设置到对象的实例(NullReferenceException),所以必须对结果进行NUL的判断,这就是关键点
第三步,按门店汇总数据
//Linq按城市、门店汇总数据
var SumHyBymdmc = from dt in JoinByDymc
group dt by new { city = dt.city , mdmc = dt.mdmc }into tb
select new
{
city = tb.Key.city,
mdmc = tb.Key.mdmc,
hy_hys = tb.Sum(dt => dt.hy_hys),
hy_hys_hb = tb.Average(dt => dt.hy_hys_hb),
yz_hys = tb.Sum(dt => dt.yz_hys),
yz_hys_hb = tb.Average(dt => dt.yz_hys_hb)
};
第四步,按城市汇总数据
代码参照按门店汇总数据.
N久没有写C#的代码,最快速的方法就是Google,不过网上也没现成的列子,都是东拼西凑,然后按自己的逻辑自己改进调整,发现Linq确实是很强大,很方便做数据处理。搞笑的是这些天在C#,ABAP,SQL,Javascript,VBA之间切换,语法常常搞混了