У меня есть такая сущность модели транзакции:

public class TransactionModel
{
    public int Id { get; set; }
    public int? Withdraw { get; set; }
    public int? Deposit { get; set; }
    public DateTime Date { get; set; }
}

Я использую класс TransactionViewModel для отображения данных, которые имеют только одно свойство больше, чем класс TransactionModel, а именно Balance

Я хочу отображать баланс при отображении каждой транзакции.

       Withdraw         Deposit         Balance

1)     -                100             100
2)     50               -               50
3)     10               -               40

Я использую метод linq Select, но когда я хочу рассчитать баланс, я не могу использовать метод Sum().

 public IEnumerable<TransactionViewModel> 
 CreateTransactionViewModel(IEnumerable<TransactionModel> transactions)
    {
        return transactions.Select(x => new TransactionViewModel
        {
            Id= x.Id,
            Date = x.Date,
            Deposit = x.Deposit,
            Withdraw = x.Withdraw,
            Balance = //...
        });
    }

Обновить

Я не уверен, что использование Sum() - лучший способ рассчитать баланс. Я просто хочу иметь баланс по каждой транзакции.

-1
mohammad 19 Июл 2017 в 15:58
С каким кодом вы не могли использовать Sum? Было бы полезно, если бы вы объяснили логику.
 – 
Tim Schmelter
19 Июл 2017 в 16:02
Покажите нам свой запрос Sum и его результат, исключение или что-то еще?
 – 
Vladimir Arustamian
19 Июл 2017 в 16:02
Баланс должен быть свойством только для получения в TransactionViewModel, выполняя разницу между Депозитом и Снятием.
 – 
Mauro Sampietro
19 Июл 2017 в 16:03
1
Вместо того, чтобы вычислять его каждый раз, когда вы выполняете этот запрос, вы, вероятно, просто хотите вычислять баланс каждый раз, когда добавляется новая транзакция, и сохранять его в БД.
 – 
Servy
19 Июл 2017 в 16:09
Изменить решение на += (x.Deposit.GetValueOrDefault() - x.Withdraw.GetValueOrDefault()) и восстановить ответ :) хороший трюк с депозитом - вывести кстати
 – 
Sergey Berezovskiy
19 Июл 2017 в 16:13

2 ответа

Вы можете рассчитать это так:

return transactions.Select(x => new TransactionViewModel
        {
            Id= x.Id,
            Date = x.Date,
            Deposit = x.Deposit,
            Withdraw = x.Withdraw,
            Balance = transactions.Where(y => y.Id <= x.Id).Sum(z => z.Deposit ?? 0 - z.Withdraw ?? 0)
        });
1
Romano Zumbé 19 Июл 2017 в 16:07
Это не работает должным образом. Вы можете увидеть результат здесь: ibb.co/f2Wrh5
 – 
mohammad
19 Июл 2017 в 16:25
Где идентификаторы в этом выводе?
 – 
Romano Zumbé
19 Июл 2017 в 17:06
Этот код делает предположения об идентификаторе и порядке записи, которые, на мой взгляд, несостоятельны. Если вы собираетесь принять заказ, можно подумать, что вы предполагаете, что это по дате desc, но, тем не менее, это предположение. Мы не знаем, как заполняется идентификатор - это может быть личность, а может и нет. Вы также рассчитываете текущий баланс, и приведенный выше пример данных не дает мне понять, должен ли он быть балансом только по строкам, общим балансом или текущим балансом. OP должен точно указать тип баланса, который нужно показать, чтобы улучшить качество ответов.
 – 
Trioj
19 Июл 2017 в 17:09
Ты прав. Это предположение было неверным. Я неверно истолковал 1), 2) и 3) как идентификаторы. Но я думаю, что этот пример явно представляет собой промежуточный итог.
 – 
Romano Zumbé
19 Июл 2017 в 17:11
@ romano-zumbé Лол, на самом деле моя проблема тоже в том, чтобы принять порядок. Я предполагаю, что смотрю снизу вверх, чтобы просмотреть историю того, что произошло, хотя на самом деле это происходит сверху вниз. Если смотреть сверху вниз, это явно текущий баланс.
 – 
Trioj
19 Июл 2017 в 17:17

Зачем использовать Linq? Вы можете сделать это с помощью простого цикла.

int balance = 0;
int row = 1;
List<BalanceModel> result = new List<BalanceModel>();
foreach(TransactionModel tm in list)
{
    balance += tm.Deposit - tm.Withdraw;
    BalanceModel bm = new BalanceModel()
    {
        Row = row,
        Withdraw = tm.Withdraw,
        Deposit = tm.Deposit,
        Balance = balance,
    };
    result.Add(bm);
    row++;
}
1
user2023861 19 Июл 2017 в 16:14
Это не проще, чем linq. Его намного больше кода
 – 
Romano Zumbé
19 Июл 2017 в 16:18
@ RomanoZumbé, там больше строк кода, но с этим проще, чем что-либо transactions.Where(y => y.Id <= x.Id).Sum(z => z.Deposit ?? 0 - z.Withdraw ?? 0)
 – 
user2023861
19 Июл 2017 в 16:20
Я не согласен. Представление linq делает более понятным, что там происходит. Вы можете почти прочитать, что происходит, как в предложении. Но это зависит от OP. Ваше решение ничем не хуже моего. Просто дело личных предпочтений
 – 
Romano Zumbé
19 Июл 2017 в 16:21
3
Мне нравится LINQ, но иногда, когда у вас есть молоток, все выглядит как гвоздь. Т.е. подсчет суммы всех предыдущих транзакций для каждой транзакции может вызвать проблемы с производительностью (но помните правило 20/80 перед оптимизацией). Конечно, вы можете зафиксировать баланс в запросе LINQ и использовать его в select, но это приведет к побочным эффектам в запросе, что тоже нехорошо. Я лично предпочитаю циклы, если я модифицирую некоторые переменные или элементы запроса.
 – 
Sergey Berezovskiy
19 Июл 2017 в 16:30
1
Если в вашей компании есть программист, который не способен читать и понимать этот код по сравнению с кодом LINQ, то я твердо убежден, что вам следует уволить этого программиста. Я не верю, что пример LINQ на самом деле более ясен, просто более краток.
 – 
Trioj
19 Июл 2017 в 17:12