0

enter image description here

The image above are my sample data of ListN.

I've another list ListA with two items in it {ID=1090,ID=1089}

Now I've the following code:

CodeA:

var x = 0;
foreach(var item in ListA){
    if(ListN.Where(x=>x.ID==item.ID).Any(y=>y.Data != null))
    {
        x = ListN.Where(x=>x.ID==item.ID).Min(y=>y.Data).Value;
        break;
    }
}

CodeB:

var x = 0;
foreach(var item in ListA){
    var list = ListN.Where(x=>x.ID==item.ID);
    if(list.Any(y=>y.Data != null))
    {
        x = list.Min(y=>y.Data).Value;
        break;
    }
}

Can anyone explain to me why CodeA is giving me error throwing null exception but CodeB working fine?

SuicideSheep
  • 5,260
  • 19
  • 64
  • 117
  • 1
    Could you post the stack trace of exception? – Dennis Mar 04 '14 at 07:56
  • on which line do you get the exception? – Nyx Mar 04 '14 at 08:08
  • On which line the exception is thrown? – Shlomi Borovitz Mar 04 '14 at 08:10
  • It looks like CodeA and CodeB should be equivalent. Are `ListA` and `ListN` simply .NET objects of type `List<>`, or is this actually Linq-To-Sql? You do capture the iteration variable `item` in lambdas, but it should be no issue (neither in C# 4 or C# 5) because those lambdas are not used outside of the loop body. Are you sure you show us everything needed to understand the problem? Can you make code (including code populating `ListA` and `ListN`) that actually reproduces the problem, and post that code here? – Jeppe Stig Nielsen Mar 04 '14 at 09:13

3 Answers3

2

Not an answer to the question. But this is an awful way to achieve what you are trying to do...

var x = (from item in ListA
        join list in ListN on item.ID equals list.ID
        where list.Data != null
        select list.Data).Min();

Is much clearer in your meaning. Plus the Linq compiler will actually produce more optimal code for you (since it will use Hash Look ups).

The main problem you are having is that you are trying to mix up imperative programming (foreach, break, etc) with declarative programming (Where, Min etc) and confusing yourself.

Aron
  • 15,464
  • 3
  • 31
  • 64
1

This is because you tested for nulls in the first If condition but in the second you are disregarding that test and selecting the items that match the Where clause again.

You are not getting the same list in the second selection as the one you got from the first test.

In CodeB sample you are only processing the items that definitely do not have null data fields.

I would suggest to do something like this :

var x = 0;
foreach(var item in ListA)
{
    var list = ListN.Where(x=> x.ID == item.ID && x.Data != null).ToList();

    if(list.Any())
    {
       x = list.Min(y=>y.Data).Value;
       break;
    }    
}
Alex
  • 1,110
  • 8
  • 15
  • 1
    This is nonsense. Given the sample data that the OP has provided, making small changes to allow the code to even compile, and running both code samples, **neither** throws an exception. Both code samples are performing exactly the same amount of filtering. – Damien_The_Unbeliever Mar 04 '14 at 08:21
  • @Damien_The_Unbeliever Read the code samples again. – Alex Mar 04 '14 at 08:37
  • 1
    @Alex - if you look at my "answer", you'll see that I've *compiled* and *run* the code samples. That tells me more than reading them will. – Damien_The_Unbeliever Mar 04 '14 at 08:38
  • @Nyx Really? That's new. – Alex Mar 04 '14 at 08:38
  • @Damien_The_Unbeliever You don't have the rest of his code. So not testing for nulls the second time he crashes some other proc. Simple as that. – Alex Mar 04 '14 at 08:39
  • 1
    @Alex .Any will simply return true in this case if "yes there is at least one item in the list that is not null". It will not filter out anything from the IEnumerable resulting from the .Where. Anyway, the .Any is used in both methods. – Nyx Mar 04 '14 at 08:42
  • @Nyx First, .Any() can be used as a filter with a lambda. Second, .Any() is a faster test than .Count > 0 in certain cases so as a matter of habit that's how I test collections. Here is an interesting discussion on the subject http://stackoverflow.com/questions/305092/which-method-performs-better-any-vs-count-0 – Alex Mar 04 '14 at 08:45
  • 2
    I agree, I don't have the rest of his code. But we have his sample data, and I've added the minimum to make it compile and run. And both pieces of code produce identical results, there's no difference in the amount of filtering performed in either one. So, wherever the problem lies, it's **not** in the code that they're showing us. – Damien_The_Unbeliever Mar 04 '14 at 08:57
  • +1 Damn, now I understand. Yes you are right. I was only trying to address the calling code exception. Didn't mean to sound like an ass mate :) – Alex Mar 04 '14 at 09:22
1

This is not an answer. But it's far too big to be a comment.

I cannot reproduce your problem. I've no idea what the two answers claiming there's some form of "more filtering" happening in one sample than the other are going on about.

Here's your code, a couple of fixups (because you can't have var x and use x as a lambda parameter) and your data. And it outputs No Exception twice, indicating that both code samples run fine:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;

namespace ConsoleApplication4
{
    class Program
    {
        class N
        {
            public int ID { get; set; }
            public int? Data { get; set; }
        }

        class A
        {
            public int ID { get; set; }
        }

        static int CodeA(List<N> ListN, List<A> ListA)
        {
            var c = 0;
            foreach (var item in ListA)
            {
                if (ListN.Where(x => x.ID == item.ID).Any(y => y.Data != null))
                {
                    c = ListN.Where(x => x.ID == item.ID).Min(y => y.Data).Value;
                    break;
                }
            }
            return c;
        }

        static int CodeB(List<N> ListN, List<A> ListA)
        {
            var c = 0;
            foreach (var item in ListA)
            {
                var list = ListN.Where(x => x.ID == item.ID);
                if (list.Any(y => y.Data != null))
                {
                    c = list.Min(y => y.Data).Value;
                    break;
                }
            }
            return c;
        }

        static void Main(string[] args)
        {
            var listN = new List<N>
            {
                new N {ID = 1090, Data = 1},
                new N {ID = 1090, Data = 3},
                new N {ID = 1090, Data = 4},
                new N {ID = 1089, Data = 1},
                new N {ID = 1089, Data = 3},
                new N {ID = 1089, Data = 4},
                new N {ID = 1089, Data = null}
            };
            var listA = new List<A>
            {
                new A {ID = 1089},
                new A {ID = 1090}
            };
            try
            {
                CodeA(listN, listA);
                Console.WriteLine("No Exception");
            }
            catch (Exception ex)
            {
                Console.WriteLine("A exception: " + ex.ToString());
            }
            try
            {
                CodeB(listN, listA);
                Console.WriteLine("No Exception");
            }
            catch (Exception ex)
            {
                Console.WriteLine("B exception: " + ex.ToString());
            }
            Console.ReadLine();
        }
    }

}

If you want some help with your actual problem, can you please take the above code sample and make appropriate changes to the point where CodeA throws this exception you talk about and CodeB doesn't. You can then incorporate this into your question and I'll delete this "answer".

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • +1, I failed to reproduce it too... For me the two methods should get the same result, but maybe the problem is in the implementation of ListA or ListN? – Nyx Mar 04 '14 at 08:24
  • 2
    The problem reads like an issue with Closure, but the code should work fine. I suspect that the OP oversimplified his example. I suggest he use LinqPad to construct a working example. – Aron Mar 04 '14 at 08:32