LINQ Group by with with with group with with groups with aggregate with multiple tables

时间:2017-01-24 07:31:55

标签: c# entity-framework linq asp.net-mvc-5

我有以下三种型号。我想加入并在我的MVC应用程序中将它们与LINQ分组,以获得所需的结果数据。

用错误的数据进行了试验

var query = from i in db.Invoices
                    join id in db.Invoice_Details
                    on i.INVOICENO equals id.INVOICENO
                    join m in db.Mixings
                    on id.INVOICEDETAILID equals m.INVOICEDETAILID into ms
                    from m in ms.DefaultIfEmpty()
                    group new { id,m } by new
                    {
                        INVOICENO = id.INVOICENO,
                        DATE = i.DATE


                    }
                    into temp
                    select new Invoice_List
                    {
                        ID = temp.Key.INVOICENO,
                        INVOICENO = temp.Key.INVOICENO,
                        CARET = temp.Sum(g => g.id.CARET),
                        DATE = temp.Key.DATE,
                        ISSUECARET = (decimal?)temp.Select(c => c.m.CARETUSED).DefaultIfEmpty(0).Sum() ?? 0,
                        AVAILABLECARET = ((decimal?)temp.Select(c => c.id.CARET).DefaultIfEmpty(0).Sum() ?? 0) - ((decimal?)temp.Select(c => c.m.CARETUSED).DefaultIfEmpty(0).Sum() ?? 0)
                    };

发票表

INVOICENO        DATE
---------------------
1                2017-01-23 00:00:00
2                2017-01-23 00:00:00

发票明细表

INVOICEDETAILID        INVOICENO        CARET
----------------------------------------------
1                      1                100.00
2                      1                200.00
3                      2                300.00
4                      2                400.00  

混合表

MIXINGNO        INVOICEDETAILID        CARETUSED
------------------------------------------------
1               1                      50.00
1               2                      100.00                
2               1                      25.00
2               2                      50.00

现在我希望通过将这三个表与group by连接来跟踪结果数据。

预期结果

INVOICENO        DATE                        TOTALCARET          CARETUSEDCARET        AVAILABLECARET
------------------------------------------------------------------------------------------
1                2017-01-23 00:00:00         300.00              225.00                75.00
2                2017-01-23 00:00:00         700.00              0.00                  700.00

错误的结果(1个INVOICENO的总计)

INVOICENO        DATE                        TOTALCARET          CARETUSEDCARET        AVAILABLECARET
------------------------------------------------------------------------------------------
1                2017-01-23 00:00:00         600.00              225.00                375.00
2                2017-01-23 00:00:00         700.00              0.00                  700.00

4 个答案:

答案 0 :(得分:3)

我写了一个例子作为对我们的评论的回复,这可能无法解决您的所有问题,但可能是一个良好的开端。这是我的测试环境:

public class Invoice
{
    public int InvoiceNo { get; set; }
    public DateTime DateTime { get; set; }
}


public class InvoiceDetails
{
    public int InvoiceDetailId { get; set; }
    public int InvoiceNo { get; set; }
    public decimal Caret { get; set; }
}

public class Mixing
{
    public int MixingNo { get; set; }
    public int InvoiceDetailId { get; set; }
    public decimal CaretUsed { get; set; }

}
private static void ExecQuery()
{
    var invoices = new List<Invoice>();
    invoices.Add(new Invoice { InvoiceNo = 1, DateTime = new DateTime(2017, 1, 23) });
    invoices.Add(new Invoice { InvoiceNo = 2, DateTime = new DateTime(2017, 1, 23) });

    var invoiceDetails = new List<InvoiceDetails>();
    invoiceDetails.Add(new InvoiceDetails { InvoiceDetailId = 1, InvoiceNo = 1, Caret = 100 });
    invoiceDetails.Add(new InvoiceDetails { InvoiceDetailId = 2, InvoiceNo = 1, Caret = 200 });
    invoiceDetails.Add(new InvoiceDetails { InvoiceDetailId = 3, InvoiceNo = 2, Caret = 300 });
    invoiceDetails.Add(new InvoiceDetails { InvoiceDetailId = 4, InvoiceNo = 2, Caret = 400 });

    var mixings = new List<Mixing>();
    mixings.Add(new Mixing { MixingNo = 1, InvoiceDetailId = 1, CaretUsed = 50 });
    mixings.Add(new Mixing { MixingNo = 2, InvoiceDetailId = 2, CaretUsed = 100 });
    mixings.Add(new Mixing { MixingNo = 3, InvoiceDetailId = 1, CaretUsed = 25 });
    mixings.Add(new Mixing { MixingNo = 4, InvoiceDetailId = 2, CaretUsed = 50 });


    // select all from invoices
    var query = from i in invoices
                // join the details
                join id in invoiceDetails on i.InvoiceNo equals id.InvoiceNo
                // group the details on invoice
                group id by new { i.InvoiceNo, i.DateTime } into ig

                // again join the details (from the mixing)
                join id in invoiceDetails on ig.Key.InvoiceNo equals id.InvoiceNo
                // join the mixing
                join mix in mixings on id.InvoiceDetailId equals mix.InvoiceDetailId into mix2 // store in temp for outer join
                from mbox in mix2.DefaultIfEmpty()
                // group mixing (and sum the caret of the previous group
                group mbox by new { ig.Key.InvoiceNo, ig.Key.DateTime, TotalCaret = ig.Sum(item => item.Caret) } into igm
                // calculate the caret used (because it is used twice in the results)
                let caretUsedCaret = igm.Where(item => item != null).Sum(item => item.CaretUsed)
                // select the results.
                select new
                {
                    igm.Key.InvoiceNo,
                    igm.Key.DateTime,
                    igm.Key.TotalCaret,
                    CaretUsedCaret = caretUsedCaret,
                    Available = igm.Key.TotalCaret - caretUsedCaret
                };


    foreach (var row in query)
    {
        Trace.WriteLine(row.ToString());
    }

}

结果显示:

{ InvoiceNo = 1, DateTime = 23-Jan-17 00:00:00, TotalCaret = 300, CaretUsedCaret = 225, Available = 75 }
{ InvoiceNo = 2, DateTime = 23-Jan-17 00:00:00, TotalCaret = 700, CaretUsedCaret = 0, Available = 700 }

答案 1 :(得分:2)

哈哈:)我用方法链写了同样的东西......

   public class Invoice
    {
        public int INVOICENO { get; set; }
        public DateTime DATE { get; set; }
    }

    public class InvoiceDetail
    {
        public int INVOICEDETAILID { get; set; }
        public int INVOICENO { get; set; }
        public int CARET { get; set; }
    }

    public class Mixing
    {
        public int MIXINGNO { get; set; }
        public int INVOICEDETAILID { get; set; }
        public int CARETUSED { get; set; }
    }

    [Fact]
    public void LinqTest()
    {
        List<int>  ints = new List<int> {1,2,3};

        List<Invoice> invoices = new List<Invoice>
        {
            new Invoice {INVOICENO = 1, DATE = DateTime.Parse("23/01/2017")},
            new Invoice {INVOICENO = 2, DATE = DateTime.Parse("23/01/2017")}
        };

        List<InvoiceDetail> invoiceDetails = new List<InvoiceDetail>
        {
            new InvoiceDetail{ INVOICEDETAILID = 1, INVOICENO = 1, CARET = 100},
            new InvoiceDetail { INVOICEDETAILID = 2, INVOICENO = 1, CARET = 200},
            new InvoiceDetail { INVOICEDETAILID = 3, INVOICENO = 2, CARET = 300},
            new InvoiceDetail {INVOICEDETAILID = 4, INVOICENO = 2, CARET = 400}
        };

        List<Mixing> mixings = new List<Mixing>
        {
            new Mixing {MIXINGNO = 1, INVOICEDETAILID = 1, CARETUSED = 50},
            new Mixing {MIXINGNO = 1, INVOICEDETAILID = 2, CARETUSED = 100},
            new Mixing {MIXINGNO = 2, INVOICEDETAILID = 1, CARETUSED = 25},
            new Mixing {MIXINGNO = 2, INVOICEDETAILID = 2, CARETUSED = 50}
        };

        var q =
            invoices.Join(invoiceDetails, i => i.INVOICENO, id => id.INVOICENO, (invoice, detail) => new {invoice, detail})
                .GroupJoin(mixings, arg => arg.detail.INVOICEDETAILID, m => m.INVOICEDETAILID,
                    (arg, m) => new {arg.invoice, arg.detail, Mixings = m})
                .GroupBy(arg => arg.invoice)
                .Select(
                    g =>
                        new
                        {
                            g.Key.INVOICENO,
                            g.Key.DATE,
                            Tot_Caret = g.Sum(arg => arg.detail.CARET),
                            Tot_Used = g.Sum(arg => arg.Mixings.Sum(mixing => mixing.CARETUSED)),
                            Available = g.Sum(arg => arg.detail.CARET) - g.Sum(arg => arg.Mixings.Sum(mixing => mixing.CARETUSED))
                        });
    }

答案 2 :(得分:2)

EF的最大特色之一是所谓的导航属性。在LINQ to Entities查询中使用时,它们提供必要的元数据,以便在将查询转换为SQL时构建必要的连接。并且允许您构建查询,就好像它们是在对象上运行一样,这基本上消除了考虑连接的需要,而是集中于您的逻辑。

假设您的模型是这样的(仅显示导航属性):

public class Invoice
{
    // ...
    public ICollection<InvoiceDetail> Details { get; set; }
}

public class InvoiceDetail
{
    // ...
    public ICollection<Mixing> Mixings { get; set; }
}

同样查看表格,似乎InvoiceNoInvoice的PK。

在这种情况下,您甚至不需要GroupBy。前两个字段来自Invoice,另一个字段来自儿童Sum

var query =
    from i in db.Invoices
    let TOTALCARET = i.Details.Sum(d => (decimal?)d.CARET) ?? 0
    let USEDCARET = i.Details.SelectMany(d => d.Mixings).Sum(m => (decimal?)m.CARETUSED) ?? 0
    select new
    {
        i.INVOICENO,
        i.DATE,
        TOTALCARET,
        USEDCARET,
        AVAILABLECARET = TOTALCARET - USEDCARET
    };

唯一的技巧是在使用Sum函数时将非可空类型提升为可空,以便在源序列为空时避免异常。然后使用null-coalescing运算符在需要时将其转换为不可为空。

答案 3 :(得分:0)

如果有人使用LINQ解决方案,请在此处发布。 @Jeroen van Langen的答案非常接近但却给我错误。

通过一些修改和尝试,我至少解决了Raw SQLQuery的问题。 LINQ没有帮助我处理复杂和嵌套的查询。 以下是我非常熟悉的Raw SQL代码,它是一个临时解决方案。根据我的最终要求,我还添加了其他几张表。

使用RAW SQL QUERY的TEMP工作解决方案

var str = "select";
        str += " a.ID,a.INVOICENO,a.TOTAL,a.CARET,a.DATE,a.PARTY,a.BROKER,";
        str += " ISNULL(b.CARET,0) as ISSUECARET,";
        str += " ISNULL(a.CARET,0) - ISNULL(b.CARET,0) as AVAILABLECARET";
        str += " from";
        str += " (";
        str += " select";
        str += "     i.INVOICENO as ID,";
        str += " i.INVOICENO,";
        str += " i.DATE,";
        str += " a.accountname as PARTY,";
        str += " b.accountname as BROKER,";
        str += " SUM(id.CARET) as CARET,";
        str += " SUM(id.TOTAL) as TOTAL";
        str += "     from invoice i";
        str += "     inner";
        str += " join Invoice_Details id";
        str += "                 on i.INVOICENO = id.INVOICENO";
        str += " inner join account a on a.ID=i.party inner join account b on b.ID=i.broker";
        str += "     group by";
        str += " i.id,";
        str += " i.INVOICENO,";
        str += " i.DATE,";
        str += " a.accountname,";
        str += " b.accountname";
        str += " )";
        str += " as a";
        str += " left join";
        str += " (";
        str += " select";
        str += "     id.INVOICENO,";
        str += " SUM(m.caret) as CARET";
        str += "     from";
        str += " Invoice_Details id";
        str += "     left";
        str += " join";
        str += " Mixing m";
        str += " on id.ID = m.INVOICEDETAILID";
        str += "     group by id.invoiceno";
        str += " )";
        str += "    as b";
        str += " on a.INVOICENO = b.INVOICENO";
        var query = db.Database.SqlQuery<Invoice_List>(str);