[英]LINQ to SQL and a running total on ordered results
我想在DataGridView
顯示客戶的會計歷史記錄,並且我想有一列顯示其余額的運行總計。 我這樣做的舊方法是獲取數據,遍歷數據,然后將行一一添加到DataGridView
並計算當時的運行總數。 瘸。 我更願意使用 LINQ to SQL 或 LINQ(如果 LINQ to SQL 不可能的話)來計算運行總數,這樣我就可以將DataGridView.DataSource
設置為我的數據。
這是我正在拍攝的一個超級簡化的例子。 假設我有以下課程。
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
可以解決問題,但它並不像我希望的那樣工作。 或者,也許我只是想不通。
這個問題似乎和我想要的一樣,但我不明白接受/唯一的答案如何解決我的問題。
關於如何解決這個問題的任何想法?
編輯結合 Alex 和 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
});
使用閉包和匿名方法:
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
}
這個怎么樣:(歸功於此來源)
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();
}
}
}
如果這還沒有得到回答,我有一個我一直在我的項目中使用的解決方案。 這與 Oracle 分區組非常相似。 關鍵是讓運行總數中的 where 子句匹配 orig 列表,然后按日期對其進行分組。
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();
Aggregate 也可用於獲取運行總計:
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;
});
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));
}
}
對此的大多數其他答案(正確設置對象內的運行總數)依賴於副作用變量,這不符合功能編碼和.Aggregate()
之類的精神。 該解決方案消除了副作用變量。
(注意 - 此解決方案將與其他答案一樣在客戶端上運行,因此可能不是您需要的最佳解決方案。)
var results = FetchDataFromDatabase()
.OrderBy(item => item.Date)
.Aggregate(new List<Item>(), (list, i) =>
{
var item = new Item
{
Amount = i.Amount,
Date = i.Date,
RunningTotal = i.Amount + (list.LastOrDefault()?.RunningTotal ?? 0)
};
return list.Append(item).ToList();
// Or, possibly more efficient:
// list.Add(item);
// return list;
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.