我想将最旧的信用发票日期映射到销售表中的列表。我有以下列表。
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...
}
客户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; });
}
注意:Sales表具有约75,000条记录。
更多信息...
客户可以比销售发票支付部分款项。所有付款和发票都过帐到另一个表,这就是为什么余额来自另一个表的复杂查询。
“销售”表仅包含原始销售数据。
现在,客户1的余额比上次销售发票甚至全部或部分最后一次销售发票的值都多1000。我需要映射“从平衡开始”。
答案 0 :(得分:0)
因此,您有两个序列:Balances
序列和Sales
序列。
每个Balance
至少具有一个int属性CustomerId
和一个BalanceValue
每个Sale
至少具有一个可为null的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
和{{ 1}},该客户具有SaleDate
的顺序。
您的要求未按在“销售”序列中有“无销售”客户的“余额”序列中指定您想要的“余额”。假设您希望结果中的项目为空Sales
。
LastSaleDate
没有客户,我们对销售不感兴趣。因此,让我们先将它们过滤掉:
IEnumerable<Balance> balances = ...
IEnumerable<Sales> sales = ...
现在,通过在CustomerId上加入group,以其销售来创建余额组:
var salesWithCustomers = sales.Where(sale => sale.CustomerId != null);
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
})
的计算将所有元素排序,并且仅保留第一个元素。
尽管此解决方案有效,但如果只需要最大的元素,则订购所有其他元素效率不高。如果您使用的是IEnumerables,而不是IQueryables(就像在数据库中一样),则可以通过创建函数来对此进行优化。
我将其实现为扩展功能。参见extension methods demystified
LastSaleDate
用法:
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;
}
}
现在您知道,除了对整个序列进行排序之外,您只需要枚举一次即可。
注意:您不能为此使用Enumerable.Aggregate,因为它不适用于空序列。