2009-12-02 28 views
33

Tôi muốn hiển thị lịch sử kế toán của khách hàng trong một số DataGridView và tôi muốn có một cột hiển thị tổng số đang chạy cho số dư của chúng. Cách cũ tôi đã làm điều này là bằng cách nhận dữ liệu, lặp qua dữ liệu và thêm hàng vào số DataGridView từng cái một và tính tổng số đang chạy tại thời điểm đó. Què. Tôi sẽ sử dụng LINQ to SQL, hoặc LINQ nếu không thể với LINQ to SQL, để tìm ra các tổng số đang chạy để tôi có thể chỉ đặt DataGridView.DataSource cho dữ liệu của mình.LINQ to SQL và tổng số đang chạy trên các kết quả đã đặt hàng

Đây là một ví dụ siêu đơn giản về những gì tôi đang chụp. Nói rằng tôi có lớp sau.

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

Tôi muốn một L2S, hoặc LINQ, tuyên bố rằng có thể tạo ra kết quả mà trông như thế này:

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 

Chú ý rằng có thể có nhiều mục có cùng ngày (12-02-2009). Các kết quả sẽ được sắp xếp theo ngày trước khi tổng số hoạt động được tính toán. Tôi đoán điều này có nghĩa là tôi sẽ cần hai câu lệnh, một để lấy dữ liệu và sắp xếp nó và một giây để thực hiện tính toán tổng chạy.

Tôi đã hy vọng Aggregate sẽ thực hiện thủ thuật, nhưng nó không hoạt động như tôi đã hy vọng. Hoặc có lẽ tôi không thể hình dung ra được.

Điều này question dường như đang diễn ra sau cùng một điều tôi muốn, nhưng tôi không thấy cách câu trả lời được chấp nhận/chỉ giải quyết được vấn đề của mình.

Bất kỳ ý tưởng nào về cách gỡ cài đặt này?

Sửa Chải câu trả lời từ Alex và DOK, đây là những gì tôi đã kết thúc với:

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

Cảm ơn công cụ mới! : RunningTotal = runningTotal + = item.Amount –

+0

Sẽ không thực thi giải pháp này trên máy khách? (tức là nó phải kéo toàn bộ kết quả để có được câu trả lời đúng?) - có vẻ như một cái gì đó như thế này sẽ có hiệu suất cao hơn nếu nó được thực hiện trên máy chủ SQL ... – BrainSlugs83

+1

Sử dụng biến bên ngoài truy vấn rất nguy hiểm! Bởi vì 'kết quả' biến là loại' IEnumerable', thực thi ** của nó sẽ bị trì hoãn ** cho đến sau này. Nếu bạn thay đổi giá trị 'runningTotal' trước đó, truy vấn kết quả của bạn sẽ không chính xác nữa. Để an toàn, bạn cần phải liệt kê nó ngay lập tức (để liệt kê hoặc mảng). Tôi không thấy bất cứ điều gì sai ở đây với việc sử dụng một vòng lặp 'foreach' đơn giản. – Alexey

Trả lời

35

Sử dụng đóng cửa và phương pháp vô danh:

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

Tôi ước tôi có thể đánh dấu cả hai bạn là câu trả lời! Câu trả lời của bạn rất dễ hiểu và phù hợp với ví dụ của tôi. Tôi làm như thế nào DOK increments 'currentTotal' inline, trong quá trình chuyển nhượng, mặc dù. – Ecyrb

+0

+1 SOF cần có các tùy chọn để đánh dấu nhiều câu trả lời. –

+4

Tôi đã thử điều này với LINQ to Entities (EF) và nhận được một "Một biểu thức lambda với một tuyên bố cơ thể không thể được chuyển đổi thành một cây biểu hiện" biên dịch lỗi. Điều đó đặc biệt đối với EF như trái ngược với L2O? –

25

Làm thế nào về điều này: (tín dụng chuyển đến 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

Cảm ơn! Tôi thích cách bạn chỉ định 'inline_total' nội dòng. Đó là khá trơn. – Ecyrb

+3

'running_total = (running_total = running_total + x)' => Tâm thổi. Tôi chắc chắn sẽ nhớ điều này trong thời gian tới :) –

+1

cool! Nó cũng làm việc với các đối tượng kiểu mạnh, không chỉ các kiểu vô danh – hagensoft

1

tổng hợp có thể được sử dụng để có được một số hoạt động cũng như:

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

Trong trường hợp này đã không được trả lời, tôi có một giải pháp mà tôi đã được sử dụng trong các dự án của tôi. Điều này khá giống với một nhóm được phân đoạn Oracle. Điều quan trọng là phải có mệnh đề where trong tổng số đang chạy khớp với danh sách orig, sau đó nhóm nó theo ngày.

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

Thú vị Có vẻ như điều này sẽ giảm tải dữ liệu xuống cơ sở dữ liệu đúng cách. – BrainSlugs83

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)); 
    } 
} 
Các vấn đề liên quan