Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Select with Ternary Operator/CASE-WHEN Server Side Evaluation #6598

Closed
nooxnet opened this issue Sep 24, 2016 · 4 comments
Closed

Query: Select with Ternary Operator/CASE-WHEN Server Side Evaluation #6598

nooxnet opened this issue Sep 24, 2016 · 4 comments
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@nooxnet
Copy link

nooxnet commented Sep 24, 2016

I wanted to select a few columns of a table/class into a smaller object. I used the ternary operator to get the value for one field. With 1.0.1 this produces a wrong SQL query.

var result = dbContext.Foos
    .OrderBy(f => f.Name)
    .ThenBy(f => f.Id)
    .Select(f => new CodeNameClass()
    {
        Id = f.Id,
        Name = f.Name,
        Code = !string.IsNullOrEmpty(f.Code) ? f.Code : f.Id.ToString()
    })
    .AsNoTracking()
    .ToList();

The generated SQL is:

SELECT [f].[Id], [f].[Name], CASE
    WHEN CASE
        WHEN [f].[Code] IS NULL OR (([f].[Code] = N'') AND [f].[Code] IS NOT NULL)
        THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
    END
    THEN CAST(0 AS BIT) ELSE CAST(1 AS BIT)
END, [f].[Code]
FROM [Foos] AS [f]
ORDER BY [f].[Name], [f].[Id]

The code worked with RC2 and 1.0. As I saw from my logs the ternary operator had been evaluated on the client side till 1.0.

Steps to reproduce

public class Foo
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
    // some further fields
}

public class ApplicationDbContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"server=(localdb)\MsSqlLocalDB;database=SampleDB");
    }
}

public class CodeNameClass
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

public class TestEfCoreServerEvaluation
{
    public TestEfCoreServerEvaluation()
    {
        using (var dbContext = new ApplicationDbContext())
        {
            Console.WriteLine("EnsureDeleted ...");
            dbContext.Database.EnsureDeleted();
            Console.WriteLine("Done!");
            Console.WriteLine("EnsureCreated ...");
            dbContext.Database.EnsureCreated();
            Console.WriteLine("Done!");

            var result = dbContext.Foos
                .OrderBy(f => f.Name)
                .ThenBy(f => f.Id)
                .Select(f => new CodeNameClass()
                {
                    Id = f.Id,
                    Name = f.Name,
                    Code = !string.IsNullOrEmpty(f.Code) ? f.Code : f.Id.ToString()
                })
                .AsNoTracking()
                .ToList();

            //var result = dbContext.Foos
            //  .OrderBy(f => f.Name)
            //  .ThenBy(f => f.Id)
            //  .Select(f => new CodeNameClass()
            //  {
            //      Id = f.Id,
            //      Name = f.Name,
            //      Code = f.Code == null ? f.Code : f.Id.ToString()
            //  })
            //  .AsNoTracking()
            //  .ToList();

            Console.WriteLine("");
        }
    }
}

The second query does not use IsNullOrEmpty. The generated SQL is:

SELECT [f].[Id], [f].[Name], [f].[Code] IS NULL, [f].[Code]
FROM [Foos] AS [f]
ORDER BY [f].[Name], [f].[Id]
Exception message:
An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.dll

Additional information: Incorrect syntax near the keyword 'IS'.

Further technical details

EF Core version: 1.0.1
Operating system: Windows 10
Visual Studio version: VS 2015

@divega divega added this to the 1.0.2 milestone Sep 26, 2016
@divega
Copy link
Contributor

divega commented Sep 26, 2016

@smitpatel this seems to be a regression in 1.0.1. Please investigate.

@smitpatel
Copy link
Contributor

We have ProjectionComparisonTransformingVisitor class in Sql-Server specific code.
https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore.SqlServer/Query/Sql/Internal/SqlServerQuerySqlGenerator.cs#L153
Which somewhat does the same thing as SearchConditionTranslatingVisitor. In combination, both tried to make a case block hence we got into state of incorrect nested case blocks.
Sql-Server specific code should be removed and relational one should be used. The most of the changes needed in SearchConditionTranslatingVisitor are already done in 1.1.0 codebase.

@smitpatel smitpatel added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 28, 2016
@divega
Copy link
Contributor

divega commented Oct 12, 2016

Reopening to go through proper approval process.

@Eilon
Copy link
Member

Eilon commented Nov 1, 2016

This patch is approved, please ensure it is merged into the correct branch and building as part of the patch train.

@divega divega changed the title Select with Ternary Operator/CASE-WHEN Server Side Evaluation Query: Select with Ternary Operator/CASE-WHEN Server Side Evaluation Dec 13, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants