Foreach or switch messing up linq query?

An attempt to create a query to filter data in the following order works fine, returning users filtered by any filters in the FilterNamesAndValues ​​parameter.

GetAllUsersFiltered(..., Dictionary<string,string> FilterNamesAndValues) { .... List<DataContracts.IUser> lstUsers = new List<DataContracts.IUser>(); .... var query = from u in lstUsers select u; string firstName = string.Empty; FilterNamesAndValues.TryGetValue("FirstName", out firstName); query = query.Where(u => u.FirstName == firstName); string company = string.Empty; FilterNamesAndValues.TryGetValue("Company", out company); query = query.Where(u => u.CompanyName == company); .... return query.ToList(); } 

The example below does not work, and I do not understand why:

 GetAllUsersFiltered(..., Dictionary<string,string> FilterNamesAndValues) { .... List<DataContracts.IUser> lstUsers = new List<DataContracts.IUser>(); .... var query = from u in lstUsers select u; foreach (KeyValuePair<string, string> kv in FilterNamesAndValues) { if (kv.Value != null) { switch (kv.Key) { case "FirstName": query = query.Where(u => u.FirstName == kv.Value); break; case "Company": query = query.Where(u => u.CompanyName == kv.Value); break; } } } return query.ToList(); } 

After the application got into the first case of the switch, I can execute query.ToList () and see the line there. But by the time the execution has passed around the loop to get into the second filter, query.ToList () returns nothing. The request is not filtered sequentially as it was in the first example, and worse than this, the filter conditions were actually lost. This is probably the obvious explanation, but right now I don't see it.

+4
source share
1 answer

The problem is that you are closing kv in foreach, but the request is being executed using deferred execution. This forces it to close the wrong value. For more information on what’s going on, I would recommend posting Eric Lippert's post titled, “Closing a loop variable that is considered harmful .”

You can solve this with a temporary:

 foreach (KeyValuePair<string, string> kvOriginal in FilterNamesAndValues) { // Make a temporary in the correct scope! KeyValuePair<string, string> kv = kvOriginal; if (kv.Value != null) { switch (kv.Key) { case "FirstName": query = query.Where(u => u.FirstName == kv.Value); break; case "Company": query = query.Where(u => u.CompanyName == kv.Value); break; } } } 
+4
source

Source: https://habr.com/ru/post/1383466/


All Articles