Pomelo.EntityFrameworkCore.MySql icon indicating copy to clipboard operation
Pomelo.EntityFrameworkCore.MySql copied to clipboard

[NotMapped] property in entity will cuased query all field in query, even not used

Open Flithor opened this issue 1 year ago • 1 comments

Steps to reproduce

public class AnyClassContainsNotMapped
{
    public int Key { get; set; }
    public string Name { get; set; }
    public byte[] NeedNotQuery { get; set; }
    [NotMapped]
    public string Display => $"{Key}-{Name}"
}

Linq query:

var names = dbContext.Set<AnyClassContainsNotMapped>().Select(o => o.Name.Contains(keyword)).Select(p => p.Name).ToList();

The issue

Check this SQL, all field has been listed.

Further technical details

MySQL version: Operating system: Windows 11 Pomelo.EntityFrameworkCore.MySql version: 8.0.0-beta.2 Microsoft.AspNetCore.App version: 8.0.1

Flithor avatar Jan 21 '24 14:01 Flithor

I am unable to reproduce this issue.

The following simple console app works as expected and only returns the Name column:

Program.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate;

public class AnyClassContainsNotMapped
{
    [Key]
    public int Key { get; set; }

    public string Name { get; set; }
    public byte[] NeedNotQuery { get; set; }

    [NotMapped]
    public string Display
        => $"{Key}-{Name}";
}

public class PreserveLastCommandTextInterceptor : DbCommandInterceptor
{
    public override InterceptionResult<DbDataReader> ReaderExecuting(
        DbCommand command,
        CommandEventData eventData,
        InterceptionResult<DbDataReader> result)
    {
        ((Context)eventData.Context).LastCommandText = command.CommandText;
        return result;
    }
}

public class Context : DbContext
{
    public DbSet<AnyClassContainsNotMapped> AnyClassContainsNotMappedSet { get; set; }

    public string LastCommandText { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=Issue1833";
            var serverVersion = ServerVersion.AutoDetect(connectionString);

            optionsBuilder
                .UseMySql(
                    connectionString,
                    serverVersion)
                .AddInterceptors(new PreserveLastCommandTextInterceptor())
                .LogTo(Console.WriteLine, LogLevel.Information)
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AnyClassContainsNotMapped>()
            .HasData(
                new AnyClassContainsNotMapped
                {
                    Key = 1,
                    Name = "Douglas",
                    NeedNotQuery = Encoding.Default.GetBytes("NoNeedToQuery"),
                },
                new AnyClassContainsNotMapped
                {
                    Key = 2,
                    Name = "Adams",
                    NeedNotQuery = Encoding.Default.GetBytes("NoNeedToQuery"),
                });
    }
}

internal static class Program
{
    private static void Main()
    {
        using var context = new Context();

        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();

        var names = context.Set<AnyClassContainsNotMapped>()
            .Where(o => o.Name.Contains("dam"))
            .Select(p => p.Name)
            .ToList();

        Trace.Assert(names.Count == 1);
        Trace.Assert(names[0] == "Adams");
        Trace.Assert(Regex.IsMatch(context.LastCommandText, @"^SELECT\s+`a`.`Name`\s+FROM"));
    }
}
Output (SQL)
info: 13.02.2024 17:44:49.609 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (55ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      DROP DATABASE `Issue1833`;

info: 13.02.2024 17:44:50.141 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE DATABASE `Issue1833`;

info: 13.02.2024 17:44:50.304 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (13ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      ALTER DATABASE CHARACTER SET utf8mb4;

info: 13.02.2024 17:44:50.348 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (43ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE `AnyClassContainsNotMappedSet` (
          `Key` int NOT NULL AUTO_INCREMENT,
          `Name` longtext CHARACTER SET utf8mb4 NULL,
          `NeedNotQuery` longblob NULL,
          CONSTRAINT `PK_AnyClassContainsNotMappedSet` PRIMARY KEY (`Key`)
      ) CHARACTER SET=utf8mb4;

info: 13.02.2024 17:44:50.356 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      INSERT INTO `AnyClassContainsNotMappedSet` (`Key`, `Name`, `NeedNotQuery`)
      VALUES (1, 'Douglas', 0x4E6F4E656564546F5175657279),
      (2, 'Adams', 0x4E6F4E656564546F5175657279);

info: 13.02.2024 17:44:51.624 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT `a`.`Name`
      FROM `AnyClassContainsNotMappedSet` AS `a`
      WHERE `a`.`Name` LIKE '%dam%'

@Flithor Please change my sample app in such a way, that it reproduces the issue that you reported. (The sample app deletes and creates the Issue1833 database on every run, so don't use a database with real data.)

lauxjpn avatar Feb 13 '24 16:02 lauxjpn

@Flithor Any update on this?

lauxjpn avatar Mar 16 '24 08:03 lauxjpn