Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to change properties of an IQueryable collection

I am trying to do what I think is something simple, but I suspect I am simply too n00b to know that I am probably doing something wrong. I have a LINQ query return:

IQueryable<CWords> Result

Where CWords is a class I defined as follows:

public class CWords
{
    public CWords(){}
    public string _column1{ get; set; }
    public float  _column2{ get; set; }

    public void fixData(){}
}

in my code, I am trying to modidy the _column2 field for each member of Result. I tried:

foreach (CWords item in Result)
{
    item.fixData();
}

But of course that didn't work. item is not in the proper scope, so any changes I was making in fixData were not taking in Result.

Bcause you cannot index into IQueryable, my fix for this was to do the following:

var items = goodWords.ToList();

for (int i = 0; i < items.Count(); i++ )
   {
    items[i].fixData();
   }

Is that the right way to do this?

like image 651
Brandon Watson Avatar asked Feb 10 '09 01:02

Brandon Watson


1 Answers

Brandon, this is a normal mistake when using LINQ. See, the IQueryable returned from LINQ does not actually contain your items, that's why you can't index into it. It has only enough information to perform the query when you actually ask for the items. This is called "deferred execution", because the query is not executed when you select, but later, when you enumerate the results. You can search for "linq deferred execution" and find lots of people trying to explain how it works.

When you do your foreach, the query runs and you are calling fixData() on each item, just as you intended. However, when you access the IQueryable again, you will execute the query a second time, and (depending on the LINQ provider you are using) you might bringing in the original un-modified items a second time.

By calling ToList() on the IQueryable, you are creating an in-memory List with all the results of the query. You can now index into this list and access it all you want without executing the query again. If you're are fine with having all the items in memory (small result set), then using ToList() can be a good solution for you.

@PaulG, you are right that he can use foreach instead of for, but he should save a reference to the list or else he's right where he started (with an IQueryable).

var items = goodWords.ToList();

foreach (var item in items)
{
    item.fixData();
}
like image 85
Lucas Avatar answered Oct 20 '22 21:10

Lucas