Bug in LinqToLLBLGenPro

Posts   
 
    
acl
User
Posts: 91
Joined: 28-Mar-2012
# Posted on: 04-Jun-2014 10:19:59   

We are using LLBLGenPro 4.0.13.0606 (SD.LLBLGen.Pro.ORMSupportClasses.dll).

Here is a query expressed in LinqToLLBLGenPro

years = (From TParJ In dbLinq.TParJ _
              Where TParJ.TParJ_Jahr < targetYear _
              Order By TParJ.TParJ_Jahr Descending
              Select TParJ.TParJ_Jahr).ToList()

where:
 - dblinq is a LinqMetadata object.
 - targetYear is a String with the value "2050"
 - TParJ_Jahr is a char(4) column (no integer!) in the database, and a string in the TParJ Entity.

This is the SQL that comes out of the query:

SELECT TOP(@p2) [LPLA_1].[TParJ_Jahr] 
FROM [TestDb_9900].[dbo].[TParJ]  [LPLA_1]  
 WHERE ( ( ( ( ( ( CASE WHEN ([LPLA_1].[TParJ_Jahr] = @p6) THEN 0 ELSE 1 END < @p4))))))
 ORDER BY [LPLA_1].[TParJ_Jahr] DESC

where:
 - @p2 = 1
 - @p6 = '2050'
 - @p8 = 0
 - @p4 = 0

The SQL query will never return any result. The result of the case statement (either 0 or 1) is never going to be smaller than the value of @p4 (which is 0).

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 04-Jun-2014 10:35:07   

The problem is caused by the case statement. It might be caused by the way you're comparing strings: doing it that way might result in unexpected results, as the VB.NET compiler likely inserts additional statements, as stringA < stringB results in a method call to string.Compare(stringA, stringB) which is then compared to 0. This is then converted to a CASE call with a comparison.

We'll see whether the case statement is correct, i.e. whether it mimics what the string.Compare() method does.

Btw, why are you comparing numbers as strings?

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 04-Jun-2014 12:09:29   

It's indeed something caused by the VB.NET compiler. (C# doesn't allow you to compare two strings with '<', you have to use string.Compare):


    <Test()> _
    Public Sub StringCompareTest()
        Using adapter As New DataAccessAdapter
            Dim metaData As New LinqMetaData(adapter)
            Dim q = From c In metaData.Customer Where c.CustomerId < "CCCCC" Select c
            Dim count As Integer = 0
            For Each v In q
                count += 1
            Next
            Assert.AreEqual(12, count)
        End Using
    End Sub

results in the expression tree:

value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[NW26.Adapter.EntityClasses.CustomerEntity]).Where(c => (CompareString(c.CustomerId, "CCCCC", False) < 0)).Select(c => c)

Which shows the CompareString() call (which is a native VB.NET method). string.Compare does work though:


    <Test()> _
    Public Sub StringCompareTest()
        Using adapter As New DataAccessAdapter
            Dim metaData As New LinqMetaData(adapter)
            Dim q = From c In metaData.Customer Where String.Compare(c.CustomerId, "CCCCC") < 0 Select c
            Dim count As Integer = 0
            For Each v In q
                count += 1
            Next
            Assert.AreEqual(12, count)
        End Using
    End Sub

results in: value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[NW26.Adapter.EntityClasses.CustomerEntity]).Where(c => (Compare(c.CustomerId, "CCCCC") < 0)).Select(c => c)

which results in the proper sql:

Generated Sql query: 
    Query: SELECT [LPA_L1].[Address], [LPA_L1].[City], [LPA_L1].[CompanyName], [LPA_L1].[ContactName], [LPA_L1].[ContactTitle], [LPA_L1].[Country], [LPA_L1].[CustomerID] AS [CustomerId], [LPA_L1].[Fax], [LPA_L1].[Phone], [LPA_L1].[PostalCode], [LPA_L1].[Region] FROM [Northwind].[dbo].[Customers]  [LPA_L1]   WHERE ( ( CASE WHEN [LPA_L1].[CustomerID] < @p4 THEN -1 WHEN [LPA_L1].[CustomerID] = @p4 THEN 0 ELSE 1 END < @p2))
    Parameter: @p4 : String. Length: 5. Precision: 0. Scale: 0. Direction: Input. Value: "CCCCC".
    Parameter: @p2 : Int32. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: 0.

Looking at the linq provider, it contains a buggy CompareString mapping. The string.Compare() mapping is different, while it should be the same. Fixing...

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 04-Jun-2014 12:29:29   

See attached dll for the fix.

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.DQE.SqlServer.dll 49,152 04-Jun-2014 12:29.36 Approved
Frans Bouma | Lead developer LLBLGen Pro