gpt4 book ai didi

c# - 为什么 LINQ 在我的查询中使用了错误的数据类型,而在 EF 架构中正确声明了它?

转载 作者:行者123 更新时间:2023-12-03 17:11:49 27 4
gpt4 key购买 nike

我的数据库架构默认使用 varchar。使用 EF(6) 代码优先方法,我通过将字符串的 ColumnType 设置为 varchar 来确保我的模型是正确的:modelBuilder.Properties<string>().Configure(p => p.HasColumnType("varchar"));
我正在使用 PredicateBuilder 来构建我的 where 子句,并且一切都按预期工作; LINQ 使用 varchar 数据类型创建参数化查询。我也尝试过不使用 PredicateBuilder:出现完全相同的问题。

但是,一旦我添加了 Select 语句,LINQ 突然决定将数据类型更改为 nvarchar,我想不出任何原因。这当然对我的查询有严重的负面影响,因为 sql server 现在必须做一堆隐式转换,使我的索引无用。它现在正在扫描表格而不是寻找。

var ciPredicate = PredicateBuilder.New<InfoEntity>(true);
ciPredicate = ciPredicate.And(x => x.InfoCode == ciCode);
ciPredicate = ciPredicate.And(x => x.Source == source);

//varchar - N'@p__linq__0 varchar(8000),@p__linq__1 varchar(8000)'
var ciQuery2 = this.Scope.Set<InfoEntity>().Where(ciPredicate).ToList();

//varchar - N'@p__linq__0 varchar(8000),@p__linq__1 varchar(8000)'
var ciQuery3 = this.Scope.Set<InfoEntity>().Where(ciPredicate).GroupBy(x => new { x.Source, x.InfoKey }).ToList();

//varchar - N'@p__linq__0 varchar(8000),@p__linq__1 varchar(8000)'
var ciQuery4 = this.Scope.Set<InfoEntity>().Where(ciPredicate).GroupBy(x => new { x.Source, x.InfoKey }).ToList().Select(group => group
.OrderByDescending(x => x.InfoSeqNr)
.FirstOrDefault()
);

//nvarchar - N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)'
var ciQueryNvarchar = this.Scope.Set<InfoEntity>().Where(ciPredicate).GroupBy(x => new { x.Source, x.InfoKey })
.Select(group => group
.OrderByDescending(x => x.InfoSeqNr)
.FirstOrDefault()
).ToList();

表定义:
CREATE TABLE Info(
Id int NOT NULL,
InfoKey int NOT NULL,
Source varchar(50) NOT NULL,
InfoCode varchar(50) NOT NULL,
InfoDesc varchar(4000) NOT NULL,
InfoSeqNr int NOT NULL
)

由于这只是查询的开始,因此我们不能将 ciQuery4 与中间的 ToList() 一起使用。

我一生都无法弄清楚为什么会发生这种情况,任何帮助将不胜感激。

最佳答案

这似乎是 EF 中的一个错误......还是真的?

我能够在 EF6.4.4 中使用您的 GroupBy 上的 select 语句重现这一点。条款。单列GroupBy的和复合柱GroupBy好像没什么区别。

但是当我改变了Select - 对这样的事情的子句

var ciQueryNvarchar = this.Scope.Set<InfoEntity>().Where(ciPredicate).GroupBy(x => new { x.Source, x.InfoKey })
.Select(group => new {
Group = group.Key,
MostRecentItem = group.OrderByDescending(x => x.InfoSeqNr).FirstOrDefault()
}).ToList();

EF 不再在生成的 SQL 语句中使用 nvarchar(4000)。所以这可能是一种解决方法,但它并不漂亮......

更好的选择是完全重写查询,因为 LINQ GroupBy和 SQL GROUP BY are different things . LINQ的 GroupBy是某种排序 + 分桶操作,而 SQL 的 GROUP BY 还结合了聚合函数,在这种情况下我们没有。

因此,上面的查询可以改写为
var ciQueryNvarchar = this.Scope.Set<InfoEntity>()
.Where(ciPredicate)
.Select(x => new { x.Source, x.InfoKey })
.Distinct()
.Select(x => this.Scope.Set<InfoEntity>()
.Where(ciPredicate)
.Where(y => y.Source == x.Source && y.InfoKey == x.InfoKey)
.OrderByDescending(y => y.InfoSeqNr)
.FirstOrDefault()
)
.ToList();

比较查询计划时,初始查询和重写的查询生成的 SQL 几乎相同,但有趣的部分在于差异可以解释类型更改为 nvarchar .

enter image description here

在 .NET 中,所有字符串都是 Unicode ( nvarchar )。所以如果生成的查询包含 SELECT @p__linq__0 AS [p__linq__0], @p__linq__1 AS [p__linq__1] ,EF 最好的猜测是使用 nvarchar使此列匹配 .NET 的 unicode 字符串。

所以它可能不是一个错误?

另一种替代方法可能是使用纯 SQL(这也会使查询更具可读性),但这可能也不理想(鉴于重命名重构和数据库架构更改)。

关于c# - 为什么 LINQ 在我的查询中使用了错误的数据类型,而在 EF 架构中正确声明了它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62215208/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com