I have an in memory list, from which I get some rows and update a field. I can't understand why the parent list is also updated. I used ToList()
, Range()
, new List<type>(filteredRows)
, nothing works. I don't want to create another list and just push item by item, because the parent is huge and can affect the performance.
private readonly IServiceScopeFactory scopeFactory;
private List<Row> rows= new List<Row>();
public InMemoryData(IServiceScopeFactory scopeFactory)
{
this.scopeFactory = scopeFactory;
}
the original rows looks like below:
{ Name: 'Team A', Position: 1, CountryId: 1},
{ Name: 'Team B', Position: 2, CountryId: 2},
{ Name: 'Team C', Position: 3, CountryId: 1},
{ Name: 'Team D', Position: 4, CountryId: 1}
The problem is in the following method:
public List<Row> GetFiltereRows(int? countryId = null) {
if (countryId != null) {
var filteredRows = rows
.Where(x => x.CountryId == (int)countryId)
.ToList();
var position = 0;
return filteredRows.Select(x => {
// here, also the entity "x" in the parent list is updated
x.Position = ++position;
return x;
}).ToList();
}
return rows;
}
When I filter the data by CountryId
1, I want that Team C
to have Position 2
and Team D
to have Position 3
(to recalculate positions only for selected country).
I think I need to set the rows list as readonly, or to create clones from it.
I can't understand why for another case, the clone works:
var filteredRows = rows;
if (countryId != null) {
filteredRows = filteredRows.Where(x => x.CountryId == countryId ).ToList();
}
here, even if the filteredRows
is declared as rows
, this will not update the original list, filtering it. I know something about immutability from another languages, due of that I'm thinking that I need to do theoriginal list immutable, to use only copies of it in another methods. But also, somethimes I need to refresh the original list, so should still be able to update it (or recreate).
I used ToList(), Range(), new List(filteredRows), nothing works.
new List<Type>(filteredRows)
will create another reference pointing to the same list, it won't create a new list and clone the items in the original (parent) list.
.ToList()
? Same thing, Actually, take a look at .ToList()
's implementation:
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) => source != null ? new List<TSource>(source) : throw Error.ArgumentNull(nameof (source));
It's just the same as new List<Type>(filteredRows)
, but throws an exception if filteredRows
is null!
"// here, also the entity "x" in the parent list is updated" this is normal
As stated, you are not deep-cloning the original items, so any modification to the filtered item is a direct modification to the original item.
I can think of two choices to overcome this issue:
rows
by countryId, you can tell how long is filteredRows
, so maybe it won't affect the performance if you deep-clone itreturn filteredRows.Select(x => {
var xClone = new Row(x); // deep clone
xClone.Position = ++position;
return xClone;
}).ToList();
GetFiltereRows
..return filteredRows.Select(x => {
x.OriginalPosition = x.Position;
x.Position = ++position;
return x;
}).ToList();
Usage:
var filteredRows = GetFiltereRows(rows);
// use the filteredRows
RefreshRows();
void RefreshRows(){
foreach(var item in rows) {
item.Position = item.OriginalPosition;
item.OriginalPosition = -1;
}
}