Linq to entity 没有生成 where 子句

Posted

技术标签:

【中文标题】Linq to entity 没有生成 where 子句【英文标题】:Linq to entities is not generating a where clause 【发布时间】:2021-12-25 00:51:05 【问题描述】:

这是一个奇怪的东西,以前从未见过这样的东西。我有一个很久没有接触过的旧项目,它使用 Linq to Entities 和代码优先进行设置。我尝试从作品中选择数据的每个实体,但生成的 SQL 不包含 where 子句。 (我可以看到 SQL Profiler 中生成了什么)所以应用程序基本上是从表中选择所有行,然后在服务器上进行过滤,这并不好。我的项目是.Net Core 2.2 使用Microsoft.EntityFrameworkCore 汇编版本2.2.6.0

我觉得这可能是配置错误,但我没有发现任何问题。有人见过这样的吗?

无论这个问题是什么,当我尝试保存 Country 实体时,它也会向我展示这个异常;这没有任何意义,因为 CountryId 是下面代码优先设置中的主键,我绝对不会分配它。

Cannot insert explicit value for identity column in table 'Country' when IDENTITY_INSERT is set to OFF.

我尝试了不同的选择变体,但都没有生成 where 子句。

var country = await _taxContext.Countries
    .FirstOrDefaultAsync(a => a.TwoLetterISOCode.Equals(country.jurisCode, StringComparison.OrdinalIgnoreCase))
    .ConfigureAwait(false);

-- generates this sql
SELECT [a].[CountryId], [a].[Name], [a].[TwoLetterISOCode]
FROM [dbo].[Country] AS [a]

var exists = await _taxContext.Countries
    .AnyAsync(a => a.TwoLetterISOCode.Equals(country.jurisCode, StringComparison.OrdinalIgnoreCase))
    .ConfigureAwait(false);

-- generates this sql
SELECT [a].[TwoLetterISOCode]
FROM [dbo].[Country] AS [a]

这是我的国家实体 POCO

public class Country

    public byte CountryId  get; private set; 
    public string Name  get; set; 
    public string TwoLetterISOCode  get; set; 

    public ICollection<Nexus> Nexi  get; set; 

我的国家配置

class CountryConfiguration : IEntityTypeConfiguration<Country>

    public void Configure(EntityTypeBuilder<Country> builder)
    
        builder.ToTable(nameof(Country), "dbo");

        builder.HasKey(ci => ci.CountryId);

        builder.Property(ci => ci.Name)
            .HasColumnType("nvarchar(75)");

        builder.Property(ci => ci.TwoLetterISOCode)
            .HasColumnType("char(2)");

        builder.HasMany(ci => ci.Nexi)
            .WithOne(ci => ci.Country);
    

还有我的 DbContext

public class TaxContext : DbContext

    public TaxContext(DbContextOptions<TaxContext> options) : base(options)
    
    

    public DbSet<Region> Regions  get; set; 
    public DbSet<Country> Countries  get; set; 
    public DbSet<Nexus> Nexi  get; set; 
    public DbSet<TaxRate> FallbackRates  get; set; 
    public DbSet<CircuitBreakerLog> CircuitBreakerLogs  get; set; 
    public DbSet<AppLog> AppLogs  get; set; 

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    
        modelBuilder
            .ApplyConfiguration(new NexusConfiguration())
            .ApplyConfiguration(new RegionConfiguration())
            .ApplyConfiguration(new CountryConfiguration())
            .ApplyConfiguration(new TaxRateConfiguration())
            .ApplyConfiguration(new CircuitBreakerLogConfiguration())
            .ApplyConfiguration(new AppLogConfiguration());
    

Startup.cs

public void ConfigureServices(IServiceCollection services)

    services.AddDbContext<TaxContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

【问题讨论】:

您的日志中是否收到任何有关客户评估的警告?我怀疑您的.Equals(....) 不受EF 支持,因此正在内存中进行评估。例如,如果你刚刚做了a.TwoLetterISOCode == country.jurisCode - 这行得通吗? @DavidG 就是这样!它不支持翻译Equals吗?当答案导致更多问题时 - 堆栈溢出;) - The LINQ expression '"where [a].TwoLetterISOCode.Equals(__country_jurisCode_0, CurrentCultureIgnoreCase)"' could not be translated and will be evaluated locally. 【参考方案1】:

您应该会在输出中看到 EF 正在本地评估您的查询的警告。这是因为您的字符串比较。虽然我支持使用string.Equals,但它不支持您使用的重载。 EF 不知道如何处理StringComparison.OrdinalIgnoreCase,因为它依赖于数据库排序规则来处理大小写、重音等。解决方案是使用简单的重载或基本的==

.FirstOrDefaultAsync(a => a.TwoLetterISOCode.Equals(country.jurisCode))

或者

.FirstOrDefaultAsync(a => a.TwoLetterISOCode == country.jurisCode)

【讨论】:

以上是关于Linq to entity 没有生成 where 子句的主要内容,如果未能解决你的问题,请参考以下文章

分组后的LINQ to Entities,COUNT和WHERE不起作用

Linq to Entities 中的动态 where 子句 (OR)

在 LINQ to Entities 中正确比较没有时间的 DatetimeOffset

Linq to Entities基础之需要熟知14个linq关键字(from,where,select,group,let,on,by...)

Linq to Entities 添加 Where 子句以在另一个表中查找 EXISTS

LINQ to Entities Perf优化