LINQ do SQL i suma uruchomiona na zamówionych wynikach

Chcę wyświetlić historię księgową klienta w DataGridView i chcę mieć kolumnę, która wyświetla sumę bieżącą dla ich salda. Stary sposób, w jaki to zrobiłem, polegał na pobieraniu danych, zapętlaniu danych i dodawaniu wierszy do DataGridView jeden po drugim i obliczaniu sumy bieżącej w tym czasie. Kiepsko. Wolałbym użyć LINQ do SQL, lub LINQ, jeśli nie jest to możliwe z LINQ do SQL, aby dowiedzieć się, uruchomione sumy, więc mogę po prostu ustawić DataGridView.DataSource do moich danych.

To jest super uproszczony przykład tego, do czego strzelam. Powiedzmy, że mam następujące zajęcia.

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

Chciałbym L2S, lub LINQ, stwierdzenie, które może generować wyniki, które wyglądają tak:

   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

Zauważ, że może być kilka pozycji z tą samą datą (12-02-2009). Wyniki powinny być sortowane według daty, zanim zostaną obliczone bieżące sumy. Zgaduję, że to oznacza, że będę potrzebował dwóch instrukcji, jednego, aby uzyskać Dane i je posortować, a drugiego, aby wykonać sumę uruchomioną obliczenia.

Miałem nadzieję Aggregate uda się, ale to nie działa tak, jak miałem nadzieję. A może po prostu nie mogłem tego rozgryźć.

To pytanie wydawało się iść po to samo, co chciałem, ale nie widzę, jak przyjęta / tylko odpowiedź rozwiązuje mój problem.

Jakieś pomysły, jak to zrobić?

Edit Przeczesując odpowiedzi od Alexa i Doka, tak się skończyło:

decimal runningTotal = 0;
var results = FetchDataFromDatabase()
    .OrderBy(item => item.Date)
    .Select(item => new Item
    {
        Amount = item.Amount,
        Date = item.Date,
        RunningTotal = runningTotal += item.Amount
    });
Author: Community, 2009-12-02

5 answers

Używając zamknięć i anonimowej metody:

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
}
 36
Author: Alex Bagnolini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-02 18:40:21

A może tak: (kredyt idzie do tego źródła )

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();
        }
    }
}
 25
Author: DOK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-02 18:40:25

Jeśli jeszcze na to nie odpowiedziałam, mam rozwiązanie, którego używam w moich projektach. Jest to dość podobne do Oracle podzielonej grupy. Kluczem jest, aby klauzula where w running total pasowała do listy oryginałów, a następnie grupowała ją według daty.

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();
 5
Author: DF5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-01 21:14:13

Agregat może być również użyty do uzyskania sumy bieżącej:

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;
});
 1
Author: Aaron Anodide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-12 19:04:31
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));
    }
}
 0
Author: daydreamer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-16 12:26:07