Skip to content

Unnecessary SQL CAST when using EF.Functions.Like with strongly-typed value-converted types #36247

@amyboose

Description

@amyboose

Description

When using EF.Functions.Like on a property mapped via a ValueConverter from any strongly-typed type to string (both struct and class), Entity Framework Core emits a redundant CAST(... AS string) (or ::text in PostgreSQL) in the generated SQL.

Your code:

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.Logging;

namespace EfInvalidSql;
public class Program
{
    public static async Task Main(params string[] args)
    {
        MyContext context = new();
        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();

        var users = await context.Set<User>()
            .Where(x => EF.Functions.Like(x.Name, "Name%"))
            .ToListAsync();
    }
}

public class User
{
    public required int Id { get; set; }
    public required FullName Name { get; set; }
}

public readonly record struct FullName
{
    public FullName(string value)
    {
        Value = value;
    }

    public string Value { get; }

    public static implicit operator string(FullName fullName)
    {
        return fullName.Value;
    }
}

public class FullNameConverter : ValueConverter<FullName, string>
{
    public FullNameConverter() : base(
        v => v.Value,
        v => new FullName(v))
    {
    }
}

public class MyContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer("Server=localhost,7438;Database=TestDb;User Id=sa;Password=RMfL3Tx%bZ5b;TrustServerCertificate=True;")
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
    }

    protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
    {
        configurationBuilder.Properties<FullName>()
            .HaveConversion<FullNameConverter>()
            .HaveColumnType("text");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>(builder =>
        {
            builder.ToTable("Users");
        });

        modelBuilder.Entity<User>()
            .HasData(
                new User { Id = 1, Name = new FullName("Name1") },
                new User { Id = 2, Name = new FullName("Name2") }
                );
    }
}

Generated SQL by MSSQL provider:

SELECT [u].[Id], [u].[Name]
FROM [Users] AS [u]
WHERE CAST([u].[Name] AS nvarchar(max)) LIKE N'Name%'

Generated SQL by Npgsql provider:

SELECT u."Id", u."Name"
FROM "Users" AS u
WHERE u."Name"::text LIKE 'Name%'

EF Core version

9.0.6

Database provider

MSSQL provider and Npgsql Entity Framework Core provider for PostgreSQL

Target framework

.NET 9.0

Operating system

Windows 11

IDE

Visual Studio 17.4.5

Metadata

Metadata

Assignees

No fields configured for Feature.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions