2009-12-02 96 views
33

我想在DataGridView显示客户的会计历史,我想有显示其资产负债运行总计列运行总计。我这样做的旧方法是获取数据,循环访问数据,并逐行添加行并计算当时的运行总数。瘸。我更愿意使用LINQ to SQL,或LINQ(如果LINQ to SQL不可能的话)来计算总运行总数,所以我可以将DataGridView.DataSource设置为我的数据。LINQ to SQL和订购的结果

这是我拍摄的一个超简单的例子。假设我有以下课程。

class Item 
{ 
    public DateTime Date { get; set; } 
    public decimal Amount { get; set; } 
    public decimal RunningTotal { get; set; } 
} 

我想一个L2S,或LINQ,可能产生的结果是这样的语句:

Date  Amount RunningTotal 
12-01-2009  5   5 
12-02-2009  -5   0 
12-02-2009  10   10 
12-03-2009  5   15 
12-04-2009 -15   0 

注意,可以有多个项目具有相同的日期(12-02-2009 )。在计算运行总数之前,结果应按日期排序。我猜这意味着我需要两条语句,一条是获取数据并对其进行排序,另一条是执行运行总计算。

我希望Aggregate会做的伎俩,但它不工作就像我希望。或者,我可能无法弄清楚。

question似乎我想同样的事情后进行下去,但我没有看到接受/只回答如何解决我的问题。

关于如何解决这个问题的任何想法?

编辑 梳理从亚历克斯和DOK的答案,这是我结束了:

decimal runningTotal = 0; 
var results = FetchDataFromDatabase() 
    .OrderBy(item => item.Date) 
    .Select(item => new Item 
    { 
     Amount = item.Amount, 
     Date = item.Date, 
     RunningTotal = runningTotal += item.Amount 
    }); 
+0

感谢您的新工具! :RunningTotal = runningTotal + = item.Amount – 2015-03-17 19:39:40

+0

此解决方案是否强制执行在客户端上? (即它必须拉下整个结果集才能得到正确的答案?) - 如果在SQL服务器上完成这样的操作,看起来像这样会更加高效... – BrainSlugs83 2015-06-25 21:14:18

+1

在查询中使用外部变量是非常危险的!因为'result'变量是'IEnumerable'类型的,所以它的**执行将被推迟**直到后面。如果您在此之前更改'runningTotal'的值,您的结果查询将不再正确。为了安全起见,你需要立即枚举它(列表或数组)。使用简单的'foreach'循环我没有看到任何错误。 – Alexey 2015-06-30 05:13:20

回答

35

使用闭包和匿名方法:

List<Item> myList = FetchDataFromDatabase(); 

decimal currentTotal = 0; 
var query = myList 
       .OrderBy(i => i.Date) 
       .Select(i => 
          { 
          currentTotal += i.Amount; 
          return new { 
              Date = i.Date, 
              Amount = i.Amount, 
              RunningTotal = currentTotal 
             }; 
          } 
        ); 
foreach (var item in query) 
{ 
    //do with item 
} 
+1

我希望我能把你们俩都当成答案!你的答案很容易理解,并与我的例子相符。不过,我确实喜欢DOK在分配期间如何递增内嵌的'currentTotal'。 – Ecyrb 2009-12-02 19:16:53

+0

+1 SOF应该有标记多个答案的选项。 – 2010-08-19 10:11:49

+4

我试过这与Linq实体(EF)并得到一个“一个语句正文lambda表达式不能转换为表达式树”编译错误。对EF而言,这与L2O相反吗? – 2011-02-18 14:40:31

25

如何:(信用去this source

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     delegate string CreateGroupingDelegate(int i); 

     static void Main(string[] args) 
     { 
      List<int> list = new List<int>() { 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 69, 2007}; 
      int running_total = 0; 

      var result_set = 
       from x in list 
       select new 
       { 
        num = x, 
        running_total = (running_total = running_total + x) 
       }; 

      foreach (var v in result_set) 
      { 
       Console.WriteLine("list element: {0}, total so far: {1}", 
        v.num, 
        v.running_total); 
      } 

      Console.ReadLine(); 
     } 
    } 
} 
+0

谢谢!我喜欢你如何分配内嵌的'running_total'。这很漂亮。 – Ecyrb 2009-12-02 19:17:39

+3

'running_total =(running_total = running_total + x)'=>头脑风暴。我一定会记得下次:) – 2009-12-02 21:52:32

+1

酷!它也适用于强类型对象,而不仅仅是匿名类型 – hagensoft 2013-07-15 17:53:06

1

骨料可用于获得运行总计还有:

var src = new [] { 1, 4, 3, 2 }; 
var running = src.Aggregate(new List<int>(), (a, i) => { 
    a.Add(a.Count == 0 ? i : a.Last() + i); 
    return a; 
}); 
5

在这种情况下,没有得到回答的是,我有我一直在用我的项目的解决方案。这与Oracle分区组非常相似。关键是让运行总计中的where子句与原始列表匹配,然后按日期对其进行分组。

var itemList = GetItemsFromDBYadaYadaYada(); 

var withRuningTotals = from i in itemList  
         select i.Date, i.Amount,  
           Runningtotal = itemList.Where(x=> x.Date == i.Date). 
                 GroupBy(x=> x.Date). 
                 Select(DateGroup=> DateGroup.Sum(x=> x.Amount)).Single(); 
+0

有趣的。看起来像这样会正确地将数据卸载到数据库。 – BrainSlugs83 2015-06-25 21:19:08

0
using System; 
using System.Linq; 
using System.Collections.Generic; 

public class Program 
{ 
    public static void Main() 
    { 
     var list = new List<int>{1, 5, 4, 6, 8, 11, 3, 12}; 

     int running_total = 0; 

     list.ForEach(x=> Console.WriteLine(running_total = x+running_total)); 
    } 
}