正在更新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中的项属性,但该属性没有';不要固步自封

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()基于数据库返回的每一行构建一个新对象。更重要的是,每当IEnumerabletaAgents被迭代时,这个对象构造就会发生

因此,更具体地解释,如果数据库中有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语句的范围之外。