Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is IEnumerable losing updated data?

Tags:

c#

linq

Could you explain to me why after executing the following code the Selected property is not updated to true?

The ListItem type used comes from System.Web.UI.WebControls namespace and is a class (not a struct.) I believed the FirstOrDefault function returns a reference to an instance which I can update and pass around in the items enumerable.

// produce list items out of the communities
IEnumerable<ListItem> items = communities.Select(community => new ListItem(community.Name, community.Id.ToString()));

// mark the right list item as selected, if needed
if (platform.CommunityId > 0)
{
    string strCommunityId = platform.CommunityId.ToString();
    ListItem selectedItem = items.FirstOrDefault(item => item.Value == strCommunityId);
    if (selectedItem != null) selectedItem.Selected = true;
}

// now items do not store any updated item!

Is that because the enumerator is executed each time a foreach is called and thus creating new items instead of returning the set containing the item I updated?

like image 895
jakubiszon Avatar asked Apr 16 '15 12:04

jakubiszon


3 Answers

The problem is that IEnumerable is not repeatable. You are performing the projection (community => new ListItem) every time it is enumerated - hence it is a new ListItem each time. Select is a non-buffered deferred projection.

You can fix everything here with the simple addition of a .ToList() to force the data into a single list;

var items = communities.Select(
    community => new ListItem(community.Name, community.Id.ToString())
).ToList();

Now that the data is in the list, you can loop over the list any number of times - it'll always be the same items, and changes will be retained.

like image 197
Marc Gravell Avatar answered Oct 11 '22 15:10

Marc Gravell


Your problem is that

IEnumerable<ListItem> items = communities
    .Select(community => new ListItem(community.Name, community.Id.ToString()));

creates an IEnumerable that's lazily evaluated -- that is, every time it is enumerated, the original communities sequence is re-enumerated and your Select projection is re-executed per item in that sequence.

If you stick a .ToList() at the end, changing the line to:

IEnumerable<ListItem> items = communities
    .Select(community => new ListItem(community.Name, community.Id.ToString()))
    .ToList();

you will observe a different result. While it is still an IEnumerable, it will no longer be a lazily evaluated one, and the changes you make in it will be observable in later iterations over the same IEnumerable.

like image 33
Rytmis Avatar answered Oct 11 '22 15:10

Rytmis


It happens, because you use Select:

IEnumerable<ListItem> items = communities
   .Select(community => new ListItem(community.Name, community.Id.ToString()));

which creates new objects every time you iterate through items.

like image 21
Wojciech Kulik Avatar answered Oct 11 '22 17:10

Wojciech Kulik