0

Say I have IQueryable<Car> cars.

I iterate it such as:

foreach (Car c in cars)
{
    c.Doors = 2;
}

Why does c.Doors contain the original value after the foreach, instead of the changed value?

Thank you in advance

Matthew Walton
  • 9,809
  • 3
  • 27
  • 36
Dante
  • 3,833
  • 4
  • 38
  • 55
  • I know if I convert it to a List before I enumerate, I get the correct results. – Dante Feb 21 '12 at 13:14
  • Either the implementation of IQueryable generates a new set on each iteration or the setter of Car.Doors is 'broken'. Please show us where IQuerable is created – Polity Feb 21 '12 at 13:16
  • IQueryable is the result of a Linq to Entities query. The setter is fine because in the Linq query, I can assign the value to the property Doors. – Dante Feb 21 '12 at 13:21
  • Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. – vulkanino Feb 21 '12 at 13:21
  • @vulkanino - In this case the collection is not modified, just the items within the collection – Polity Feb 21 '12 at 13:24

2 Answers2

1

You indicated that IQueryable is the result of a linq to entities query which is not a correct statement. MyDatabaseContext.Cars.Where(x => x.Name == "Test") returns a IQueryable which on iteration perfoms a query on the database. (Iteration is when you perform a foreach over it). So it doesnt contain the result set yet, just the query.

looping twice over cars generates 2 identical queries to the database and returns 2 identical result sets. If you want to preserve the data. you need to call ToArray or ToList or after manipulation, perform a save of the changes before you iterate again.

Polity
  • 14,734
  • 2
  • 40
  • 40
  • Maybe it was poor phrasing on my part, but I know the IQueryable by itself doesn't execute the query. So, when I iterate it the query is executed once. If I do a ToList() after the iteration, the query will be executed again. Right? – Dante Feb 21 '12 at 13:30
  • Correct, ToList internally iterates the source as well :) – Polity Feb 21 '12 at 13:35
1

Iterating over an IQueryable retrieves the result set from the database as it goes, as I'm sure you know. What I've observed in this and other situations is that this result is not cached, so you'll often find that iterating over the IQueryable again will actually run the query again, thus your modifications aren't preserved because you're getting a new result set which was never affected by that code.

The reason it works when you call ToList() first and iterate over the result of that is because ToList() retrieves and materialises the entire result set, which is then no longer linked to the database content and is really just a copy of what the database returned.

The only way to be sure your changes will stick is to operate on the local copy of the data, i.e. lift it out of IQueryable land. This can be as simple as saving an IEnumerable of the result set (i.e. the result of ToEnumerable()), which will then return the same result set each time you enumerate it, but unlike ToList() it won't cause evaluation immediately.

Matthew Walton
  • 9,809
  • 3
  • 27
  • 36