AK_
AK_

Reputation: 8099

Bug in .Net's LINQ, or me missing something?

I had a strange bug i don't understand, and changing LINQ's IEnumerable to list half way through fixed it, and i dont understand why

Not Real the Code, but very similar The code below doesn't work:

// an IEnumerable of some object (Clasess) internally an array
var ansestors = GetAnsestors();

            var current = GetCurrentServerNode();

            var result = from serverNode in ansestors
                         select new PolicyResult
                                    {
                                      //Some irrelevant stuff 
                                        OnNotAvailableNode = NodeProcessingActionEnum.ContinueExecution,
                                    };

            var thisNode = new PolicyResult
            {
                //Some irrelevant stuff 
                OnNotAvailableNode = NodeProcessingActionEnum.ThrowException,
            };

            result = result.Reverse();
            result = result.Concat(new List<PolicyResult> { thisNode });

            result.First().OnNotAvailableNode = NodeProcessingActionEnum.ThrowException;

            // When looking in the debugger, and in logs, the first element of the
            // result sequence has OnNotAvailableNode set to ContinueExecution
            // Which doesnt make any sense...

But when i change the ending to the following it works:

        result = result.Reverse();
        result = result.Concat(new List<PolicyResult> { thisNode });

        var policyResults = result.ToList();
        var firstPolicyResult = policyResults.First();

        firstPolicyResult.OnNotAvailableNode = NodeProcessingActionEnum.ThrowException;
        return policyResults;

All the types here are classes (reference types) except NodeProcessingActionEnum which is an enum.

Is this a bug?

Me missing something crucial about LINQ?

Help?

Upvotes: 2

Views: 163

Answers (2)

Henk Holterman
Henk Holterman

Reputation: 273844

result.First() executes the (deferred / lazy) query.

That line will set the value OK but when you use result later the query will be executed again.

Later you are looking at a newly fetched copy. The fact that it is different lets me assume that GetAnsestors() is also lazily evaluated and is not an in memory List<>

This means that ToList() is a worthwhile optimization as well as a fix. Note that after the ToList you can also use

 var firstPolicyResult = policyResults[0];  

Upvotes: 5

Rup
Rup

Reputation: 34418

The problem is that running First on your IEnumerable removes it from the enumerator so you've then checking the next element. Actually I've changed my mind - that's probably not it. This solution might be worth a shot, though.

You could wrap the IEnumerable with something which makes the change for you, e.g. using the Select override which accepts an index too:

var modifiedResults = results.Select((r, index) => {
    if (index == 0) {
        // This is the first element
        r.OnNotAvailableNode = NodeProcessingActionEnum.ThrowException;
    }
    return r;
});

(untested) should do the trick.

Upvotes: 0

Related Questions