Bug #121034 Regression in ef core Contains using StringComparison.InvariantCultureIgnoreCase

Submitted: 30 Jul 7:54 Modified: 4 Aug 22:16
Reporter: Manuel Zulian Email Updates:
Status: Documenting Impact on me:
None 
Category:Connector / NET Severity:S3 (Non-critical)
Version:26.7.0 OS:Any
Assigned to: Jose Ramirez Ruiz CPU Architecture:Any

[30 Jul 7:54] Manuel Zulian
Description:
Using Contains with StringComparison.InvariantCultureIgnoreCase causes "THREW System.InvalidOperationException Expression [SqlExpressions.SqlFunctionExpression]", was working fine on .net 8.

How to repeat:
using System.Reflection;
using Microsoft.EntityFrameworkCore;

namespace CollateRepro;

public class Car
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class ReproContext : DbContext
{
    private readonly string _connectionString;

    public ReproContext(string connectionString) => _connectionString = connectionString;

    public DbSet<Car> Cars { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseMySQL(_connectionString);

        // Set REPRO_SQL=1 to print the generated SQL. On net8.0 this shows the COLLATE
        // construct the provider emits for the StringComparison overload.
        if (Environment.GetEnvironmentVariable("REPRO_SQL") == "1")
            optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information)
                .EnableSensitiveDataLogging();
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Car>(e =>
        {
            e.ToTable("cars");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            // varchar under the schema's default (case-insensitive) collation.
            e.Property(x => x.Name).HasColumnName("name").HasColumnType("varchar(255)").IsRequired();
        });
    }
}

public static class Program
{
    public static int Main()
    {
        var connectionString = Environment.GetEnvironmentVariable("REPRO_CONNSTR")
            ?? "server=localhost;port=3399;user id=root;password=repro;database=repro_db;";

        var efVersion = typeof(DbContext).Assembly.GetName().Version;
        var providerVersion = typeof(MySQLDbContextOptionsExtensions).Assembly.GetName().Version;

        Console.WriteLine($"Runtime                       : {Environment.Version}");
        Console.WriteLine($"Microsoft.EntityFrameworkCore : {efVersion}");
        Console.WriteLine($"MySql.EntityFrameworkCore     : {providerVersion}");
        Console.WriteLine();

        using var db = new ReproContext(connectionString);
        db.Database.EnsureDeleted();
        db.Database.EnsureCreated();
        db.Cars.Add(new Car { Id = 20, Name = "TEST2" });
        db.SaveChanges();

        // Search term is lower-case, stored value is upper-case, so a match proves the
        // comparison is case-insensitive.
        const string term = "test2";

        var plain = RunCase(
            "Contains(term)  -- no StringComparison",
            () => db.Cars.Where(f => f.Name.Contains(term)).Select(f => f.Name).ToList());

        var withComparison = RunCase(
            "Contains(term, StringComparison.InvariantCultureIgnoreCase)",
            () => db.Cars
                .Where(f => f.Name.Contains(term, StringComparison.InvariantCultureIgnoreCase))
                .Select(f => f.Name).ToList());

        Console.WriteLine();
        if (plain && withComparison)
        {
            Console.WriteLine("RESULT: both queries succeeded -- issue NOT reproduced.");
            return 0;
        }

        if (plain && !withComparison)
        {
            Console.WriteLine("RESULT: issue REPRODUCED -- only the StringComparison overload fails.");
            return 1;
        }

        Console.WriteLine("RESULT: unexpected -- the plain Contains query also failed.");
        return 2;
    }

    private static bool RunCase(string label, Func<List<string>> query)
    {
        Console.WriteLine($"--- {label}");
        try
        {
            var rows = query();
            Console.WriteLine($"    OK, matched {rows.Count} row(s): [{string.Join(", ", rows)}]");
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"    THREW {ex.GetType().FullName}");
            Console.WriteLine($"    {ex.Message}");
            return false;
        }
    }
}

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net8.0;net10.0</TargetFrameworks>
    <Nullable>disable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <AssemblyName>CollateRepro</AssemblyName>
    <RootNamespace>CollateRepro</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="MySql.EntityFrameworkCore" Version="10.0.9" />
  </ItemGroup>

</Project>
[4 Nov 3:47] MySQL Admin
Posted by developer: Bug status updated to 'Patch pending'
[5 Nov 21:47] MySQL Admin
Posted by developer: Bug status updated to 'Documenting'

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.056 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.