Linq Collection gets reset on next iteration of foreach - c#

I have the following foreach expression within which I am building a predicate and then filtering the collection by executing .Where().
But what stumps me is, result.Count() gives me 0 even before I execute .Where() in the next iteration.
var result = SourceCollection;
foreach (var fieldName in FilterKeys)
{
if (!conditions.ContainsKey(fieldName)) continue;
if (!conditions[fieldName].IsNotNullOrEmpty()) continue;
var param = conditions[fieldName];
Func<BaseEntity, bool> predicate = (d) => fieldName != null && d.GetFieldValue(fieldName).ContainsIgnoreCase(param);
result = result.Where(predicate);
}
Does, anybody know of any LINQ behavior that I might have overlooked that is causing this?

I think, you want this:
var result = SourceCollection;
foreach (var fieldName in FilterKeys)
{
if (!conditions.ContainsKey(fieldName)) continue;
if (!conditions[fieldName].IsNotNullOrEmpty()) continue;
var param = conditions[fieldName];
var f = fieldName
Func<BaseEntity, bool> predicate = (d) => f != null && d.GetFieldValue(f).ContainsIgnoreCase(param);
result = result.Where(predicate);
}
Notice the use of f in the predicate. You don't want to capture the foreach variable. In your original code, when the second iteration starts, param is still the captured value from the first iteration, but fieldName has changed.

I agree that the issue comes from not capturing the foreach variable, but there is a deeper issue and that is the combination of LINQ with imperative control flow structures - i.e. mixing LINQ & foreach in the first place.
Try this instead:
var predicates =
from fieldName in FilterKeys
where conditions.ContainsKey(fieldName)
let param = conditions[fieldName]
where param.IsNotNullOrEmpty()
select (Func<BaseEntity, bool>)
(d => fieldName != null
&& d.GetFieldValue(fieldName).ContainsIgnoreCase(param));
var result = predicates.Aggregate(
SourceCollection as IEnumerable<BaseEntity>,
(xs, p) => xs.Where(p));
No foreach loop needed and the code stays purely in LINQ. Friends shouldn't let friends mix imperative and functional... :-)

Related

Inside of a lamba expression trying to divide two values and compare to another value

C# - Inside of a lambda expression trying to divide two values and compare to another value, after checking that they are not equal to null. Doesn't seem to work in lambda expression but works in (what I think ) is equivalent foreach expression. By work I mean none of the test data satisfies the expression; the behavior of foreach code represents this (value of found after foreach code executes remains false, however, the list in the lambda expression will contain elements after code executes). I am kinda new to lambda and linq expressions, what is going on here?
linq expression is as follows:
IEnumerable<DataModel> list = dataList.Where(t => t.A != null
&& t.B != null
&& t.Compute1.Value.CompareTo((t.B.Value / t.A.Value)) != 0);
This does work in following foreach statement:
var found = false;
foreach (var data in dataList)
{
if (data.A == null || data.B == null )
{
continue;
}
var value = (double)data.B.Value / data.A.Value;
if (data.Compute1.Value.CompareTo(value) != 0)
{
found = true;
}
}
Update: I fixed this issue by casting t.Compute1, t.A, and t.B to double even though VS tells me cast is redundant.
Those are not equivalent pieces of code. Your foreach loop stops searching the moment it finds a data element where either A or B is null. Your lambda checks all the elements even if a previous one had A == null or B == null. The closest foreach equivalent would be to replace the break with a continue.
var found = false;
foreach (var data in dataList)
{
if (data.A == null || data.B == null )
{
continue;
}
var value = (double)data.B.Value / data.A.Value;
if (data.Compute1.Value.CompareTo(value) != 0)
{
found = true;
}
}

Linq query referencing objects and a string array

I am having some trouble in converting the following code to use LINQ.
int occurs = 0;
foreach (string j in items)
{
if (!string.IsNullOrEmpty(j))
{
WorkflowModule tempWM = new WorkflowModule(j);
if (tempWM.StateID == item.StateID)
{
occurs++;
}
}
}
return occurs;
So far, I have:-
var lstItems = (from lstItem in items
where !string.IsNullOrEmpty(lstItem)
let objWorkflowModule = new WorkflowModule(lstItem)
select new
{
tempWM = objWorkflowModule.StateID
}).Where(item.StateID == tempWM));
return lstItems.Count();
but intellisense is not liking the line '.Where(item.StateID == tempWM))'
Can anyone help me achieve this?
Thanks.
When you use the method syntax, you need to use a lambda on the Where operator:
...
}).Where(x => x.tempWM == item.StateID));
In other words, you need to "declare" the variable x which holds the result of the previous part of the query.
It doesn't look like item is initialized anywhere in your statement.
Here's how I'd do this
var lstItems = from lstItem in items
where !string.IsNullOrEmpty(lstItem)
let objWorkflowModule = new WorkflowModule(lstItem)
select objWorkflowModule.StateID;
return lstItems.Count(t=> t == item.StateID);
I'm assuming item is a variable defined outside of the original code you submitted. Basically you don't need to create the anonymous class in the query and you can put the predicate in you're Where into Count instead. But as others have said the main issue is that you need to express your predicate as a lambda.

Compose LINQ-to-SQL predicates into a single predicate

(An earlier question, Recursively (?) compose LINQ predicates into a single predicate, is similar to this but I actually asked the wrong question... the solution there satisfied the question as posed, but isn't actually what I need. They are different, though. Honest.)
Given the following search text:
"keyword1 keyword2 ... keywordN"
I want to end up with the following SQL:
SELECT [columns] FROM Customer
WHERE (
Customer.Forenames LIKE '%keyword1%'
OR
Customer.Forenames LIKE '%keyword2%'
OR
...
OR
Customer.Forenames LIKE '%keywordN%'
) AND (
Customer.Surname LIKE '%keyword1%'
OR
Customer.Surname LIKE '%keyword2%'
OR
....
OR
Customer.Surname LIKE '%keywordN%'
)
Effectively, we're splitting the search text on spaces, trimming each token, constructing a multi-part OR clause based on each , and then AND'ing the clauses together.
I'm doing this in Linq-to-SQL, and I have no idea how to dynamically compose a predicate based on an arbitrarily-long list of subpredicates. For a known number of clauses, it's easy to compose the predicates manually:
dataContext.Customers.Where(
(
Customer.Forenames.Contains("keyword1")
||
Customer.Forenames.Contains("keyword2")
) && (
Customer.Surname.Contains("keyword1")
||
Customer.Surname.Contains("keyword2")
)
);
In short, I need a technique that, given two predicates, will return a single predicate composing the two source predicates with a supplied operator, but restricted to the operators explicitly supported by Linq-to-SQL. Any ideas?
You can use the PredicateBuilder class
IQueryable<Customer> SearchCustomers (params string[] keywords)
{
var predicate = PredicateBuilder.False<Customer>();
foreach (string keyword in keywords)
{
// Note that you *must* declare a variable inside the loop
// otherwise all your lambdas end up referencing whatever
// the value of "keyword" is when they're finally executed.
string temp = keyword;
predicate = predicate.Or (p => p.Forenames.Contains (temp));
}
return dataContext.Customers.Where (predicate);
}
(that's actually the example from the PredicateBuilder page, I just adapted it to your case...)
EDIT:
Actually I misread your question, and my example above only covers a part of the solution... The following method should do what you want :
IQueryable<Customer> SearchCustomers (string[] forenameKeyWords, string[] surnameKeywords)
{
var predicate = PredicateBuilder.True<Customer>();
var forenamePredicate = PredicateBuilder.False<Customer>();
foreach (string keyword in forenameKeyWords)
{
string temp = keyword;
forenamePredicate = forenamePredicate.Or (p => p.Forenames.Contains (temp));
}
predicate = PredicateBuilder.And(forenamePredicate);
var surnamePredicate = PredicateBuilder.False<Customer>();
foreach (string keyword in surnameKeyWords)
{
string temp = keyword;
surnamePredicate = surnamePredicate.Or (p => p.Surnames.Contains (temp));
}
predicate = PredicateBuilder.And(surnamePredicate);
return dataContext.Customers.Where(predicate);
}
You can use it like that:
var query = SearchCustomers(
new[] { "keyword1", "keyword2" },
new[] { "keyword3", "keyword4" });
foreach (var Customer in query)
{
...
}
Normally you would chain invocations of .Where(...). E.g.:
var a = dataContext.Customers;
if (kwd1 != null)
a = a.Where(t => t.Customer.Forenames.Contains(kwd1));
if (kwd2 != null)
a = a.Where(t => t.Customer.Forenames.Contains(kwd2));
// ...
return a;
LINQ-to-SQL would weld it all back together into a single WHERE clause.
This doesn't work with OR, however. You could use unions and intersections, but I'm not sure whether LINQ-to-SQL (or SQL Server) is clever enough to fold it back to a single WHERE clause. OTOH, it won't matter if performance doesn't suffer. Anyway, it would look something like this:
<The type of dataContext.Customers> ff = null, ss = null;
foreach (k in keywords) {
if (keywords != null) {
var f = dataContext.Customers.Where(t => t.Customer.Forenames.Contains(k));
ff = ff == null ? f : ff.Union(f);
var s = dataContext.Customers.Where(t => t.Customer.Surname.Contains(k));
ss = ss == null ? s : ss.Union(s);
}
}
return ff.Intersect(ss);

Foreach loop problem for IQueryable object

Can we use foreach loop for IQueryable object?
I'd like to do something as follow:
query = IQueryable<Myclass> = objDataContext.Myclass; // objDataContext is an object of LINQ datacontext class
int[] arr1 = new int[] { 3, 4, 5 };
foreach (int i in arr1)
{
query = query.Where(q => (q.f_id1 == i || q.f_id2 == i || q.f_id3 == i));
}
I get a wrong output as each time value of i is changed.
The problem you're facing is deferred execution, you should be able to find a lot of information on this but basically none of the code s being executed until you actually try to read data from the IQueryable (Convert it to an IEnumerable or a List or other similar operations). This means that this all happens after the foreach is finished when i is set to the final value.
If I recall correctly one thing you can do is initialize a new variable inside the for loop like this:
foreach (int i in arr1)
{
int tmp = i;
query = query.Where(q => (q.f_id1 == tmp || q.f_id2 == tmp || q.f_id3 == tmp));
}
By putting it in a new variable which is re-created each loop, the variable should not be changed before you execute the IQueryable.
You dont need a for each, try it like this:
query = objDataContext.Myclass.Where(q => (arr1.Contains(q.f_id1) || arr1.Contains(q.f_id2) || arr1.Contains(q.f_id3));
this is because "i" is not evaluated until you really use the iterate the query collectionif not by that time I believe "i" will be the last.

Modifying properties in object with LINQ and functions Lamba

Friends,
I know how to deploy and retrieve a single element in LINQ, but how can I do to change all the properties in a list. In the line below, I can only modify a record, I would modify several.
_ListaAcaoMenuInfo.Where(p => p.Id_acao == id).FirstOrDefault().Id_menu = 0;
Thanks
Use the ForEach function of a List...
_ListaAcaoMenuInfo.Where(p => p.Id_acao == id).ToList().ForEach(item=>item.Id_menu=0);
You wouldn't want to. LINQ is not to be used for side effects. There's a foreach loop for that.
foreach (var x in collection.where(x => x.Foo = "Blah"))
x.Foo = "Bar";
Use foreach:
var l = _ListaAcaoMenuInfo.Where(p => p.Id_acao == id).ToList();
foreach (Thing i in l)
{
i.Id_menu = 0;
//now use your Context object to save back to the database
}

Categories