как использовать LINQ TakeWhile и присоединиться к списку - PullRequest
0 голосов
/ 20 декабря 2018

Я хочу отобразить самую старую дату выставления счета-фактуры в список из таблицы продаж.У меня есть список следующим образом.

var tb = // Some other method to get balances.
cust    balance
1           1000
2           2000
3           3000
... so on ...

Этот баланс представляет собой накопление нескольких счетов или иногда может быть одним счетом.

public class Sales
{
    public int id { get; set; }
    public DateTime saleDate { get; set; }
    public int? cust { get; set; }
    public decimal invoiceValue { get; set; }
    // Other properties...
}

Пример данных для cust 1

saleInvNo  saleDate     cust invoiceValue 
1          2018/12/01   1   500
12         2018/12/20   1   750

Если я получу сейчас, отчет должен выглядеть следующим образом.

cust    balance     balanceFromDate
1          1000     2018/12/01 // balance from this onwards.
2          2000     ???
3          3000     ???

Есть ли простой способ сделать это через LINQ.

Я пытался с TakeWhile, но попытка не удалась.

foreach (var t in tb)
{
    var sum = 0m;
    var q = _context.SaleHeaders.Where(w=>w.cust == t.cust)
        .OrderByDescending(o=>o.saleDate)
    .Select(s => new { s.id, s.saleDate, s.invoiceValue }).AsEnumerable()
    .TakeWhile(x => { var temp = sum; sum += x.invoiceValue; return temp > t.balance; });
}

Примечание. Таблица продаж содержит ~ 75 тыс. Записей.

Подробнее ...

Клиенты могут оплатить частичную сумму, чем Счет-фактура на продажу,Все платежи, счета-фактуры проводятся в другую таблицу, поэтому сальдо поступает из сложного запроса к другой таблице.

Таблица продаж содержит чисто необработанные данные продаж.

Теперь баланс cust 1 на 1000 больше, чемзначение последней или последней накладной продажи полностью или частично.Мне нужно сопоставить «баланс с вперед».

1 Ответ

0 голосов
/ 20 декабря 2018

Итак, у вас есть две последовательности: последовательность Balances и последовательность Sales.

Каждый Balance имеет как минимум свойство int CustomerId и BalanceValue

Каждый Sale имеет по крайней мере обнуляемое свойство int CustomerId и свойство DateTime SaleDate

class Balance
{
     public int CustomerId {get; set;}
     public decimal BalanceValue {get; set;}
}
class Sale
{
     public int? CustomerId {get; set;}
     public DateTime SaleDate {get; set;}
} 

Очевидно, что продажа возможна без покупателя.

Теперь, учитывая последовательность Balances и Sales, вы хотите для каждого Balance одного объекта, содержащего CustomerId, BalanceValue и SaleDate, которые этот Клиент имеет в последовательности Sales.

В вашем требовании не указано, что вы хотите с Балансами в вашей последовательности Балансов, у которых есть Клиент без Продаж в последовательности Продаж.Предположим, вы хотите, чтобы эти элементы в вашем результате имели нулевое значение LastSaleDate.

IEnumerable<Balance> balances = ...
IEnumerable<Sales> sales = ...

. Мы не заинтересованы в продажах без покупателя.Итак, давайте сначала отфильтруем их:

var salesWithCustomers = sales.Where(sale => sale.CustomerId != null);

Теперь создадим группы сальдо с их продажами по groupjoiningon CustomerId:

var balancesWithSales = balances.GroupJoin(salesWithCustomers, // GroupJoin balances and sales
    balance => balance.CustomerId,       // from every Balance take the CustomerId
    sale => sale.CustomerId,             // from every Sale take the CustomerId. I know it is not null,
    (balance, salesOfBalance) => new     // from every balance, with all its sales
    {                                    // make one new object  
         CustomerId = balance.CustomerId,
         Balance = balance.BalanceValue,

         LastSaleDate = salesOfBalance                // take all salesOfBalance
            .Select(sale => sale.SaleDate)            // select the saleDate
            .OrderByDescending(sale => sale.SaleDate) // order: newest dates first
            .FirstOrDefault(),                        // keep only the first element
    })

Расчет LastSaleDate упорядочивает все элементы, идержит только первое.

Хотя это решение работало, неэффективно заказывать все остальные элементы, если вам нужен только самый большой элемент.Если вы работаете с IEnumerables, а не с IQueryables (как в базе данных), вы можете оптимизировать это, создав функцию.

Я реализую это как функцию расширения.См. демистифицированные методы расширения

static DateTime? NewestDateOrDefault(this IEnumerable<DateTime> dates)
{
    IEnumerator<DateTime> enumerator = dates.GetEnumerator();
    if (!enumerator.MoveNext())
    {
        // sequence of dates is empty; there is no newest date
        return null;
    }
    else
    {   // sequence contains elements
        DateTime newestDate = enumerator.Current;
        while (enumerator.MoveNext())
        {   // there are more elements
            if (enumerator.Current > newestDate)
               newestDate = enumerator.Current);
        }
        return newestDate;
    }
}

Использование:

LastSaleDate = salesOfBalance
    .Select(sale => sale.SaleDate)
    .NewesDateOrDefault();

Теперь вы знаете, что вместо сортировки полной последовательности вы будете перечислять последовательность только один раз.

Примечание: вы не можете использовать Enumerable.Aggregate для этого, потому что он не работает с пустыми последовательностями.

...