c# – Linq运行总共添加了第一个值

前端之家收集整理的这篇文章主要介绍了c# – Linq运行总共添加了第一个值前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有以下计算客户帐户状态的运行总计,但是他的第一个值总是被添加到自己,我不知道为什么 – 虽然我怀疑我错过了一些明显的事情:
decimal? runningTotal = 0;
    IEnumerable<StatementModel> statement = sage.Repository<FDSSLTransactionHistory>()
        .Queryable()
        .Where(x => x.CustomerAccountNumber == sageAccount)
        .OrderBy(x=>x.UniqueReferenceNumber)
        .AsEnumerable()
        .Select(x => new StatementModel()
        {
            SLAccountId = x.CustomerAccountNumber,TransactionReference = x.TransactionReference,SecondReference = x.SecondReference,Currency = x.CurrencyCode,Value = x.GoodsValueInAccountCurrency,TransactionDate = x.TransactionDate,TransactionType = x.TransactionType,TransactionDescription = x.TransactionTypeName,Status = x.Status,RunningTotal = (runningTotal += x.GoodsValueInAccountCurrency)
        });

哪些输出

29/02/2012 00:00:00 154.80  309.60  
30/04/2012 00:00:00 242.40  552.00  
30/04/2012 00:00:00 242.40  794.40  
30/04/2012 00:00:00 117.60  912.00

第一行的309.60应该是简单的154.80

我做错了什么?

编辑:
根据下面的ahruss的评论,我在我的View中对结果调用了Any(),导致第一次被评估两次 – 将我附加的ToList()解析为我的查询.

感谢大家的建议

解决方法

调用结束时添加ToList(),以避免重复调用选择器.

这是具有副作用的有状态LINQ查询,这本质上是不可预测的.在代码的其他地方,你调用了一些导致第一个元素被评估的东西,像First()或Any().一般来说,在LINQ查询中产生副作用是很危险的,当你发现自己需要它们的时候,是时候考虑它是否应该是一个foreach.

编辑或为什么会发生这种情况?

这是LINQ查询被评估的结果:直到你实际使用查询的结果,没有什么真正发生的集合.它不评估任何元素.相反,它存储Abstract Expression Trees或只需要评估查询所需的代理.然后,只有当需要结果时,才会评估这些结果,除非您明确存储结果,否则将被丢弃,并在下次重新评估.

所以这个问题为什么每次都有不同的结果?答案是,runTotal仅在第一次初始化.之后,它的值是上次执行查询后的值,这可能会导致奇怪的结果.

这意味着问题可能很容易就是“为什么总是总是两倍呢?”如果asker做这样的事情:

Console.WriteLine(statement.Count()); // this enumerates all the elements!
foreach (var item in statement) { Console.WriteLine(item.Total); }

因为获取序列中元素数量的唯一方法是实际评估它们.

同样,在这个问题上,实际发生的是某个地方有这样的代码

if (statement.Any()) // this actually involves getting the first result
{ 
    // do something with the statement
}
// ...
foreach (var item in statement) { Console.WriteLine(item.Total); }

这似乎是无害的,但是如果你知道LINQ和IEnumerable如何工作,你知道.Any()与.GetEnumerator()基本相同.MoveNext(),这使得它更明显地需要获得第一个元素.

所有这一切归结为LINQ是基于延迟执行的事实,这就是为什么解决方案是使用ToList,它规避并强制立即执行.

猜你在找的C#相关文章