Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Identical (?) C# and VB.NET LINQ queries return different results

Tags:

c#

.net

vb.net

linq

Probably something really easy, but I just can't see it...

I am replicating an MS Access query in LINQ. I wrote it in C# first to test it, because I prefer C#, then I translated it to VB.Net syntax. As far as I can tell the two queries should be identical, but whilst the C# query returns the correct results, the VB.NET one returns zero results.

Can anybody see where the difference might be?

The C# query:

var table1 = dc.MainTable.Where(o => o.Year == 423).ToList().Select(o => new
{
    Key_ID = o.Key_ID.Value,
    CropID = o.CropID.Value,
    GroupID = o.GroupID.Value,
    Surface1 = o.Surface1.Value,
    Surface2 = o.Surface2.Value
});

var table2 = dc.OtherTable.Where(o => o.Year == 423).ToList().Select(o => new
{
    Key_ID = o.Key_ID.Value,
    CropID = int.Parse(o.SAKU_CD),
    GroupID = int.Parse(o.SAN_DAN_NO),
    Surface1 = Convert.ToDouble(o.KEIHAN_MEN.Value),
    Surface2 = Convert.ToDouble(o.SAKU_MEN.Value)
});

var output = table1.Join(table2, t1 => new 
{ 
    t1.Key_ID, 
    t1.CropID, 
    t1.GroupID, 
    t1.Surface1, 
    t1.Surface2 
}, 
t2 => new 
{ 
    t2.Key_ID, 
    t2.CropID, 
    t2.GroupID, 
    t2.Surface1, 
    t2.Surface2 
}, (t1, t2) => new OutputDataType() 
{ 
    Key_ID = t1.Key_ID, 
    Year = 423 
}).ToList();

The VB.NET query:

Dim table1 = MainTable.Where(Function(o) o.Year.Value = 423).ToList().Select(Function(o) New With
{
    .Key_ID = o.Key_ID.Value,
    .CropID = o.CropID.Value,
    .GroupID = o.GroupID.Value,
    .Surface1 = o.Surface1.Value,
    .Surface2 = o.Surface2.Value
}).ToList()

Dim table2 = OtherTable.Where(Function(o) o.Year.Value = 423).ToList().Select(Function(o) New With
{
    .Key_ID = o.Key_ID.Value,
    .CropID = Convert.ToInt32(o.SAKU_CD),
    .GroupID = Convert.ToInt32(o.SAN_DAN_NO),
    .Surface1 = Convert.ToDouble(o.KEIHAN_MEN.Value),
    .Surface2 = Convert.ToDouble(o.SAKU_MEN.Value)
}).ToList()

Dim output = table1.Join(table2, Function(t1) New With
{
    t1.Key_ID,
    t1.CropID,
    t1.GroupID,
    t1.Surface1,
    t1.Surface2
}, Function(t2) New With
{
    t2.Key_ID,
    t2.CropID,
    t2.GroupID,
    t2.Surface1,
    t2.Surface2
}, Function(t1, t2) New OutputDataType With {.Key_ID = t1.Key_ID, .Year = 423}).ToList()

In both C# and VB.Net table1 and table2 are the same, so it must be the Join which fails.

EDIT

I just changed the Join in VB.Net to query syntax, like this:

Dim output = From t1 In MainTable
                 Join t2 In OtherTable
                 On t1.Key_ID Equals t2.Key_ID And t1.GroupID Equals t2.GroupID And t1.CropID Equals t2.CropID And t1.Surface1 Equals t2.Surface1 And t1.Surface2 Equals t2.Surface2
                 Select New OutputDataTypeData With {.Key_ID = t1.Key_ID, .Year = 423}

Which gives the correct result. But I really don't get how this is different from the extension method Join syntax?

like image 367
yu_ominae Avatar asked Feb 18 '13 01:02

yu_ominae


People also ask

What does =~ mean in C?

The ~ operator in C++ (and other C-like languages like C and Java) performs a bitwise NOT operation - all the 1 bits in the operand are set to 0 and all the 0 bits in the operand are set to 1. In other words, it creates the complement of the original number.

What does %d mean in C?

%d. a decimal integer (assumes base 10) %i. a decimal integer (detects the base automatically)

Is C similar to C+?

C++ is a superset of C, so both languages have similar syntax, code structure, and compilation. Almost all of C's keywords and operators are used in C++ and do the same thing. C and C++ both use the top-down execution flow and allow procedural and functional programming.

What is the question mark in C?

The question mark operator, ?:, is also found in C++. Some people call it the ternary operator because it is the only operator in C++ (and Java) that takes three operands. If you are not familiar with it, it's works like an if-else, but combines operators.


1 Answers

When you use the Join extension method, the keys you provide as outerKeySelector and innerKeySelector arguments are compared using the Equals method.

But C# and VB.Net handle anonymous types differently here:

C#

var a = new {Foo = 1, Bar = 2 };
var b = new {Foo = 1, Bar = 2 };
bool result = a.Equals(b); // true

VB.Net

Dim a = new with {.Foo = 1, .Bar = 2}
Dim b = new with {.Foo = 1, .Bar = 2}
Dim result = a.Equals(b) ' False '

What's happening here?

C# uses value equality to compare the two objects, comparing the values of the properties.

VB.Net uses reference equality to compare the two objects, hence the result is False.

To have your code work, you have to explicitly tell VB.Net to compare the properties using the Key keyword:

Key properties differ from non-key properties in several fundamental ways:

  • Only the values of key properties are compared in order to determine whether two instances are equal.
  • The values of key properties are read-only and cannot be changed.
  • Only key property values are included in the compiler-generated hash code algorithm for an anonymous type.
Dim a = new with {Key .Foo = 1, Key .Bar = 2}
Dim b = new with {Key .Foo = 1, Key .Bar = 2}
Dim result = a.Equals(b) # True

The query syntax works because in this case you are not comparing the anonymous types/objects but simply ints and doubles.

like image 187
sloth Avatar answered Nov 15 '22 21:11

sloth