简体   繁体   English

具有联接,分组依据并随后映射到列表对象的C#LINQ语句

[英]C# LINQ statement with joins, group by and having then mapped into list object

I have a model called ElectricityBillSiteExceeding that looks like this: 我有一个名为ElectricityBillSiteExceeding的模型,如下所示:

public class ElectricityBillSiteExceeding
{
    public string GroupInvoiceNumber { get; set; }
    public int ElectricityBillMainId { get; set; }
    public string SiteNo { get; set; }
    public decimal BillSiteTotal { get; set; }
    public decimal MaximumAmount { get; set; }
}

I want to create a list of this type and use it to feed a grid on one of my pages, the purpose is to show which site has bills that exceed the max amount allowed. 我想创建这种类型的列表,并用它来填充我的页面之一上的网格,目的是显示哪个网站的账单超出了允许的最大数量。

I have written the SQL which will give me this dataset, it looks like this: 我已经编写了SQL,它将给我这个数据集,它看起来像这样:

SELECT SUM(ElectricityBillSiteTotal),
       ebs.ElectricityBillMainId, 
       SiteNo, 
       ebm.GroupInvoiceNumber, 
       es.MaximumAmount
FROM dbo.ElectricityBillSites ebs
LEFT JOIN dbo.ElectricityBillMains ebm 
ON ebs.ElectricityBillMainId = ebm.ElectricityBillMainId
LEFT JOIN dbo.ElectricitySites es 
ON ebs.SiteNo = es.SiteNumber
GROUP BY ebs.ElectricityBillMainId, SiteNo, ebm.GroupInvoiceNumber, es.MaximumAmount
HAVING SUM(ElectricityBillSiteTotal) <> 0 AND SUM(ElectricityBillSiteTotal) > es.MaximumAmount

I'm now in my repository trying to write the method which will go to the database and fetch this dataset so that I can power my grid for the user to see. 我现在在我的存储库中尝试编写该方法,该方法将进入数据库并获取此数据集,以便可以为自己的网格供电,以供用户查看。

This is where I'm struggling. 这就是我努力的地方。 I have written a basic LINQ statement to select from a couple of tables, however I'm unsure how I can incorporate the group by and having clause from my SQL and also how I can then turn this IQueryable object into my List<ElectricityBillSiteExceeding> object. 我已经写了一个基本的LINQ语句从几个表中进行选择,但是我不确定如何从SQL中合并group by和having子句,以及如何将IQueryable对象变成List<ElectricityBillSiteExceeding>对象。

What I have so far 到目前为止我有什么

public List<ElectricityBillSiteExceeding> GetAllElectricityBillSiteExceedings()
{
    var groupedBillSitesThatExceed = from billSites in _context.ElectricityBillSites

    join billMains in _context.ElectricityBillMains on billSites.ElectricityBillMainId equals
    billMains.ElectricityBillMainId

    join sites in _context.ElectricitySites on billSites.SiteNo equals sites.SiteNumber

    //TODO: group by total, mainId, siteNo, GroupInv, MaxAmt and Having no total = 0 and total > max

    select new 
    {
        groupInv = billMains.GroupInvoiceNumber,
        mainId = billMains.ElectricityBillMainId,
        siteNo = billSites.SiteNo,
        total = billSites.ElectricityBillSiteTotal,
        max = sites.MaximumAmount
    };

    //TODO: Map the result set of the linq to my model and return

    throw new NotImplementedException();
}

Can anyone point me in the right direction here? 有人可以在这里指出正确的方向吗?

Before getting into grouping , you need to be aware that the default join in LINQ is always an INNER JOIN . 进入分组之前,你需要知道的是,默认join在LINQ始终是一个INNER JOIN Take a look at the MSDN page How to: Perform Left Outer Joins . 看看MSDN页面如何:执行左外部联接 However, in the solution I'm presenting below, I'm using INNER JOIN s since you are using fields from the other tables in your grouping and having clauses. 但是,在下面介绍的解决方案中,由于您正在使用分组和具有子句中其他表中的字段,因此我正在使用INNER JOIN

For reference on grouping using LINQ, check out How to: Group Query Results on MSDN . 有关使用LINQ进行分组的参考,请查看如何:在MSDN上对查询结果进行分组

A solution specific to your case is going to look something like: 针对您的案例的解决方案如下所示:

public List<ElectricityBillSiteExceeding> GetAllElectricityBillSiteExceedings()
{
    var qryGroupedBillSitesThatExceed = from billSites in _context.ElectricityBillSites
                                        join billMains in _context.ElectricityBillMains on billSites.ElectricityBillMainId equals billMains.ElectricityBillMainId
                                        join sites in _context.ElectricitySites on billSites.SiteNo equals sites.SiteNumber
                                        where billSites.ElectricityBillSiteTotal != 0 && billSites.ElectricityBillSiteTotal > sites.MaximumAmount
                                        group new { billMains.GroupInvoiceNumber, billMains.ElectricityBillMainId, billSites.SiteNo, billSites.ElectricityBillSiteTotal, sites.MaximumAmount }
                                        by new { billMains.GroupInvoiceNumber, billMains.ElectricityBillMainId, billSites.SiteNo, billSites.ElectricityBillSiteTotal, sites.MaximumAmount } into eGroup
                                        select eGroup.Key;
    var inMemGroupedBillSitesThatExceed = qryGroupedBillSitesThatExceed.AsEnumerable();
    var finalResult = inMemGroupedBillSitesThatExceed.Select(r => new ElectricityBillSiteExceeding()
    {
        BillSiteTotal = r.ElectricityBillSiteTotal,
        ElectricityBillMainId = r.ElectricityBillMainId,
        GroupInvoiceNumber = r.GroupInvoiceNumber,
        MaximumAmount = r.MaximumAmount,
        SiteNo = r.SiteNo,
    });
    return finalResult.ToList();
}

The correct Linq query for your sql is the following. 下面是您的sql的正确Linq查询。 See Left Join to understand the DefaultIfEmpty and also the notes there about the use of ?. 请参阅左联接以了解DefaultIfEmpty以及有关使用?.的注释?. in the following group by . 在以下group by

(About the having - in linq you just provide a where after the group by ) (关于having -在LINQ你只是提供了一个wheregroup by

var result = from ebs in ElectricityBillSites
             join ebm in ElectricityBillMains on ebs.ElectricityBillMainId equals ebm.ElectricityBillMainId into ebmj
             from ebm in ebmj.DefaultIfEmpty()

             join es in ElectricitySites on ebs.SiteNo equals es.SiteNumber into esj
             from es in esj.DefaultIfEmpty()

             group new { ebs, ebm, es } by new {  ebs.ElectricityBillMainId, ebs.SiteNo, ebm?.GroupInvoiceNumber, es?.MaximumAmount } into grouping
             let sum = grouping.Sum(item => item.ebs.ElectricityBillSiteTotal)
             where sum > 0 && sum > grouping.Key.MaximumAmount

             orderby sum descending

             select new ElectricityBillSiteExceeding
             {
                 GroupInvoiceNumber = grouping.Key.GroupInvoiceNumber,
                 ElectricityBillMainId = grouping.Key.ElectricityBillMainId,
                 SiteNo = grouping.Key.SiteNo,
                 BillSiteTotal = sum,
                 MaximumAmount = grouping.Key.MaximumAmount
             };

The error you get: 您得到的错误:

An expression tree lambda may not contain a null propagating operator 表达式树lambda不得包含空传播运算符

By reading this I conclude that you have an older versino of the provider and thus replace the group by code from the code above with the following: 通过阅读此内容,我得出的结论是您拥有提供者的较早版本,因此将上述代码替换为以下代码组:

let GroupInvoiceNumber = ebm == null ? null : ebm.GroupInvoiceNumber
let MaximumAmount = es == null ? 0 : es.MaximumAmount
group new { ebs, ebm, es } by new { ebs.ElectricityBillMainId, ebs.SiteNo, GroupInvoiceNumber, MaximumAmount } into grouping

This probably will be enough. 这可能就足够了。 You could use AutoMapper. 您可以使用AutoMapper。 It will trivialize mapping to classes. 它将简化对类的映射。

var resultList = groupedBillSitesThatExceed
        .AsEnumerable() //Query will be completed here and loaded from sql to memory
        // From here we could use any of our class or methods
        .Select(x => new ElectricityBillSiteExceeding
        {
            //Map your properties here
        })
        .ToList(); //Only if you want List instead IEnumerable
return resultList;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM