5

I'm trying to use the Concat method of an IEnumerable inside a for each loop, but I can't get it working properly.

IEnumerable<Geo> geos = null;
foreach (string a in values)
{
    if (geos == null)
        geos = entities.Geos.Where(g => (g.ACode == Convert.ToInt16(a)));
    else
        geos = geos.Concat(entities.Geos.Where(g => (g.ACode == Convert.ToInt16(a))));
}

What it returns is only values for the final "a" in values that too for the count of records present in values.

So if i have 1,2,3 as values, it return only for 3. I need the value for 1,2 and 3 as well.

Where am I going wrong?

Julian
  • 33,915
  • 22
  • 119
  • 174
Surendra Mourya
  • 593
  • 3
  • 8
  • 29
  • 1
    I would check that `entities.Geos.Where(g => (g.ACode == Convert.ToInt16(a)))` is actually returning rows for 1 and 2. Also, what version of Visual Studio are you using, the beahvior of variable captures in a foreach loop changed in 2013 – Scott Chamberlain Sep 02 '16 at 14:11
  • 1
    @Scott Chamberlain it is returning values for both 1 and 2, i am using VS 2010 – Surendra Mourya Sep 02 '16 at 14:15

2 Answers2

8

It is likely you are using a older version of C#, in C# 5 (ships with Visual Studio 2013) they changed the behavior of foreach. In C# 4 the a in g => (g.ACode == Convert.ToInt16(a)) would be the last value of the foreach when evaluated lazely, in C# 5 and newer it will always be the current value.

To get the C# 5 behavior you just need to declare a extra varaible inside the scope of the foreach loop and use that in the capture.

IEnumerable<Geo> geos = null;
foreach (string a in values)
{
    string b = a;
    if (geos == null)
        geos = entities.Geos.Where(g => (g.ACode == Convert.ToInt16(b)));
    else
        geos = geos.Concat(entities.Geos.Where(g => (g.ACode == Convert.ToInt16(b))));
}

If you are curious the thing that changed is in C# 4 and below your original code gets translated in to

IEnumerable<Geo> geos = null;
using(IEnumerator<string> enumerator = values.GetEnumerator())
{
    string a;
    while(enumerator.MoveNext())
    {
        a = enumerator.Current;

        if (geos == null)
            geos = entities.Geos.Where(g => (g.ACode == Convert.ToInt16(a)));
        else
            geos = geos.Concat(entities.Geos.Where(g => (g.ACode == Convert.ToInt16(a))));
    }
}

In C# 5 and newer it gets translated to

IEnumerable<Geo> geos = null;
using(IEnumerator<string> enumerator = values.GetEnumerator())
{
    while(enumerator.MoveNext())
    {
        string a = enumerator.Current;

        if (geos == null)
            geos = entities.Geos.Where(g => (g.ACode == Convert.ToInt16(a)));
        else
            geos = geos.Concat(entities.Geos.Where(g => (g.ACode == Convert.ToInt16(a))));
    }
}

By doing the string b = a; in C# 4 we re-create that behavior of the declaration being inside of the while loop.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
1

From what I understand what you want to do is have all the Geos that the ACode. Instead of going over the list of Geos for every a you can do it this way:

var intValues = values.Select(value => Convert.ToInt16(value));
var geos = entities.Geos.Where(g => intValues.Contains(g.ACode));
Gilad Green
  • 36,708
  • 7
  • 61
  • 95