无法从 IEnumerable<Model> 更改 foreach 中的值

Posted

技术标签:

【中文标题】无法从 IEnumerable<Model> 更改 foreach 中的值【英文标题】:Can't change value in foreach from IEnumerable<Model> 【发布时间】:2019-01-09 06:26:02 【问题描述】:

如何从IEnumerable&lt;Model&gt; 更改foreach 中对象的值。

代码:

public IEnumerable<Model> ListDaftarPjsp()

    IEnumerable<Model> list = from x in db.PJSPEvaluation
                              select new Model
                              
                                  foo = x.Foo,
                                  bar = x.Bar               
                              ;
    foreach (Model item in list) 
        item.condition = "example";
    
    return list;


public class Model
    public string foo get; set; 
    public string bar  get; set; 
    public string condition get; set; 

我已经创建了Model。然后我使用foreach循环结果,然后设置它。但是condition的返回值仍然没有改变?如何在 foreach 中设置条件然后返回结果

【问题讨论】:

你怎么知道,它没有改变? 您不能更改 foreach 循环内的属性列表,您必须从 LINQ 中返回它:from x in db.PJSPEvaluation select new Model foo = x.Foo, bar = x.Bar, condition = "example" ; IEnumerable&lt;T&gt; 创建一个视图。在您的示例中,每次循环通过 list 时,您似乎都在从数据库中读取数据。 google "deferred execution": list 只是一个查询,而不是该查询的答案。使用foreach 执行该查询。但随后您返回list,即原始查询,因此任何人再次迭代该查询再次执行,再次创建新的Models... 您需要在更新属性之前实现一个查询:var materializedList = list.ToList(); 然后循环遍历materializedList 并更新condition 【参考方案1】:

IEnumerable&lt;T&gt; 是一个查询,而不是一个集合。虽然在另一端有某种集合,但查询本身不是集合。您所定位的集合的性质将决定您是否可以修改内容。

一般的经验法则是,您不能期望 IEnumerable&lt;T&gt; 两次返回相同的对象列表,或者甚至不能期望您能够多次枚举它 - 这是完全有效的(如果不寻常的)IEnumerable&lt;T&gt; 仅枚举一次并拒绝枚举第二次或第三次。

在这种情况下,您所拥有的实际上是一个IQueryable&lt;Model&gt; 类型的数据库查询,它被强制转换为IEnumerable&lt;Model&gt;。它仍然是IQueryable&lt;Model&gt;,这意味着每次您枚举它时,您都会(可能)获得相同的数据列表,但在全新的对象中。更改其中一个对象不会更改同一源记录的所有对象,也不会更改基础记录本身的内容。

如果您尝试修改返回的对象而不更改底层记录(似乎是这种情况),那么您需要将查询具体化为内存中的集合。有几种方法可以做到这一点,具体取决于您希望如何处理返回的数据。

最简单的是使用.ToArray()扩展方法将查询转换为数组:

public Model[] ListDaftarPjsp()

    var query = from x in db.PJSPEvaluation
                select new Model
                
                    foo = x.Foo,
                    bar = x.Bar               
                ;

    var list = query.ToArray();

    foreach (Model item in list) 
    
        item.condition = "example";
    

    return list;

现在记录在内存中的一个数组中,可以多次枚举该数组,返回相同的确切对象,而不是每次都从数据库中获取数据的新副本。

【讨论】:

"您所定位的集合的性质将决定您是否可以修改内容。"你知道这取决于什么吗? @BorisD 是的。引用类型的已实现集合(数组、列表等)通常会将引用传递给包含的对象。如果这些对象是可变的,您可以更新它们。从流、数据库、网络连接或其他来源即时构造对象的枚举可能会为您提供新构造的对象,然后将其丢弃,因此不会保留更改。【参考方案2】:

在这里,您尝试使用LINQ 创建Model 列表,然后您正在迭代相同的内容以向每个项目添加附加属性。那么为什么不在创建列表时添加属性而不是额外的循环呢?通过尝试这样的方式让事情变得简单:

from x in db.PJSPEvaluation
select new Model

   foo = x.Foo,
   bar = x.Bar,
   condition = GetCondition(x.Foo)              
;

GetCondition() 可以定义为:

private string GetCondition(int foo)

   if(item.foo == 1) 
    
      return "a";
   
   else if(item.foo == 2) 
    
      return "b";
   
   else
   
      return "xx";    
   

【讨论】:

【参考方案3】:

已经有这个主题,但有更有效的方法可以做到这一点。 只需使用 List 而不是 Array[]。

  public List<Model> ListDaftarPjsp()
    
        List<Model> list = from x in db.PJSPEvaluation
                                  select new Model
                                  
                                      foo = x.Foo,
                                      bar = x.Bar               
                                  ;

        foreach (Model item in list) 
        
            item.condition = "example";
        
        return list;
    

    public class Model
        public string foo get; set; 
        public string bar  get; set; 
        public string condition get; set; 
    

【讨论】:

【参考方案4】:

如果您不想使用 .ToArray.ToList 将项目加载到内存中

您可以使用 Linq 中的.Select

return myEnumeration.Select(item => 
   item.condition = "example";
   return item;
)

【讨论】:

以上是关于无法从 IEnumerable<Model> 更改 foreach 中的值的主要内容,如果未能解决你的问题,请参考以下文章

从 IEnumerable<model> 到控制器的模型,无需重新加载页面 aspnet 核心

无法从“System.Collections.IList”转换为“System.Collections.Generic.IEnumerable<T>”

在 ASP.NET Core MVC Razor 视图中,与 @model IEnumerable<Namespace.Model> Vs @model<Namespace.Mode

无法将类型“IEnumerable<T>”隐式转换为“ActionResult<IEnumerable<T>>”

将数组转换为 IEnumerable<T>

从 IEnumerable 转换为 IEnumerable<object>