正在更新IEnumerable中的项属性,但该属性没有';不要固步自封
本文关键字:属性 固步自封 IEnumerable 更新 | 更新日期: 2023-09-27 18:22:14
我有两个表:Transactions和TransactionAgents。TransactionAgents有一个名为TransactionID的事务外键。相当标准。
我还有这个代码:
BrokerManagerDataContext db=新的BrokerManagerData Context();
var transactions = from t in db.Transactions
where t.SellingPrice != 0
select t;
var taAgents = from ta in db.TransactionAgents
select ta;
foreach (var transaction in transactions)
{
foreach(var agent in taAgents)
{
agent.AgentCommission = ((transaction.CommissionPercent / 100) * (agent.CommissionPercent / 100) * transaction.SellingPrice) - agent.BrokerageSplit;
}
}
dataGridView1.DataSource = taAgents;
基本上,TransactionAgent有一个名为AgentCommission的属性/列,对于我的数据库中的所有TransactionAgent,它都是null。
我的目标是执行您在foreach(var agent in taAgents)
中看到的数学运算,以修补每个代理的值,使其不为空。
奇怪的是,当我在agent.AgentCommission = (formula)
上运行此代码和断点时,它显示正在为AgentCommission计算值,并且对象正在更新,但在它显示在我的数据网格中(仅用于测试)后,它不显示它计算的值。
所以,在我看来,属性似乎并没有被永久地设置在对象上。更重要的是,如果我通过更新将这个新更新的对象保留回数据库,我怀疑计算的AgentCommission是否会设置在那里。
如果没有以同样的方式设置我的表,有没有人可以查看代码,看看我为什么不保留房产的价值?
IEnumerable<T>
s不保证更新的值将在枚举中持久存在。例如,List
将在每次迭代中返回相同的对象集,因此如果您更新属性,它将在迭代中保存。然而,IEnumerable
的许多其他实现每次都返回一组新的对象,因此所做的任何更改都不会持久存在。
如果需要存储和更新结果,请使用.ToList()
将IEnumerable<T>
下拉到List<T>
,或者使用.Select()
将其投影到新的IEnumerable<T>
中并应用更改。
为了将其具体应用于您的代码,它看起来像这样:
var transactions = (from t in db.Transactions
where t.SellingPrice != 0
select t).ToList();
var taAgents = (from ta in db.TransactionAgents
select ta).ToList();
foreach (var transaction in transactions)
{
foreach(var agent in taAgents)
{
agent.AgentCommission = ((transaction.CommissionPercent / 100) * (agent.CommissionPercent / 100) * transaction.SellingPrice) - agent.BrokerageSplit;
}
}
dataGridView1.DataSource = taAgents;
具体来说,问题是每次访问IEnumerable时,它都会枚举集合。在这种情况下,集合是对数据库的调用。在第一部分中,您将从数据库中获取值并对其进行更新。在第二部分中,您将再次从数据库中获取值,并将其设置为数据源(或者,学究般地,您将枚举器设置为数据来源,然后从数据库中获得值)。
使用.ToList()或类似的方法将结果保存在内存中,并每次访问相同的集合。
假设您使用的是LINQ to SQL,如果EnableObjectTracking为false,则每次运行查询时都会构造新的对象。否则,您每次都会得到相同的对象实例,并且您的更改将继续存在。但是,正如其他人所展示的那样,不需要多次执行查询,而是将结果缓存在列表中。你不仅可以得到你想要的工作,你还可以减少数据库往返的次数。
我发现我必须在列表中找到我想要修改的项目,提取副本,修改副本(通过增加其count属性),从列表中删除原始副本并添加修改后的副本。var x=统计数据。其中(d=>d.word==s).FirstOrDefault();var statCount=统计数据。IndexOf(x);x.count++;统计数据。RemoveAt(statCount);统计数据。加(x);
使用lambdas重写LINQ表达式很有帮助,这样我们就可以更明确地考虑代码。
//Original code from question
var taAgents = from ta in db.TransactionAgents
select ta;
//Rewritten to explicitly call attention to what Select() is actually doing
var taAgents = db.TransactionAgents.Select(ta => new TransactionAgents(/*database row's data*/)});
在重写后的代码中,我们可以清楚地看到Select()
是基于数据库返回的每一行构建一个新对象。更重要的是,每当IEnumerable
taAgents
被迭代时,这个对象构造就会发生。
因此,更具体地解释,如果数据库中有5个TransactionAgents
行,在下面的示例中,TransactionAgents()
构造函数总共被调用10次。
// Assume there are 5 rows in the TransactionAgents table
var taAgents = from ta in db.TransactionAgents
select ta;
//foreach will iterate through the IEnumerable, thus calling the TransactionAgents() constructor 5 times
foreach(var ta in taAgents)
{
Console.WriteLine($"first iteration through taAgents - element {ta}");
}
// these first 5 TransactionAgents objects are now out of scope and are destroyed by the GC
//foreach will iterate through the IEnumerable, thus calling the TransactionAgents() constructor 5 MORE times
foreach(var ta in taAgents)
{
Console.WriteLine($"second iteration through taAgents - element {ta}");
}
// these second 5 TransactionAgents objects are now out of scope and are destroyed by the GC
正如我们所看到的,我们的所有10个TransactionAgent对象都是由Select()
方法中的lambda创建的,并且不存在于foreach
语句的范围之外。