实体框架从表达式生成错误的参数类型

Posted

技术标签:

【中文标题】实体框架从表达式生成错误的参数类型【英文标题】:Entity Framework generating wrong parameter type from expression 【发布时间】:2019-07-31 23:03:58 【问题描述】:

EF 正在创建一个接收 int 作为参数的查询。它应该是一个 varchar。

我有这个生成表达式的 C# 代码

Expression<Func<Documento, bool>> query = (t => (string)t.NumeroDocumento.ToString() == (string)numeroOriginal.ToString());

var documento = documentoRepository.Obter(query, propriedadesIncluidas: "PapelPessoa.Pessoa");

Documento.Numero documento 是一个字符串。数据库表中的对应字段是 varchar(50)。 numeroOriginal 也是一个字符串。

以及存储库中的 obter (get) 代码

private T Obter(Expression<Func<T, bool>> filtro, string propriedadesIncluidas)
    
        IQueryable<T> query = dbSet;

        if (filtro != null)
            query = query.Where(filtro);

        if (!string.IsNullOrWhiteSpace(propriedadesIncluidas))
        
            foreach (var includeProperty in propriedadesIncluidas.Split
                (new char[]  ',' , StringSplitOptions.RemoveEmptyEntries))
                query = query.Include(includeProperty);
        

        return query.FirstOrDefault();
    

当 EF 创建 sql 查询时,它会创建这个查询(从调试器获得)

SELECT     [Extent1].[id] AS [id],     [Extent1].[numero_documento] AS [numero_documento],     
-- Removed for clariry
FROM [dbo].[DOCUMENTO] AS [Extent1]    
WHERE [Extent1].[numero_documento] = (CASE WHEN (@p__linq__0 IS NULL) THEN N'' ELSE @p__linq__0 END)

在参数被替换后,这将转换为

SELECT     [Extent1].[id] AS [id],     [Extent1].[numero_documento] AS [numero_documento],     
-- Removed for clariry
FROM [dbo].[DOCUMENTO] AS [Extent1]    
WHERE [Extent1].[numero_documento] = 47837

正确的查询应该是

SELECT     [Extent1].[id] AS [id],     [Extent1].[numero_documento] AS [numero_documento],     
-- Removed for clariry
FROM [dbo].[DOCUMENTO] AS [Extent1]    
WHERE [Extent1].[numero_documento] = '47837'

两者都有效,但第二个比第一个快得多(我的意思是很多)。我都在 SQL 管理工作室中运行。 EF 获取参数类型错误?我该如何解决?我需要更改代码或数据库吗?我对表的 EF 配置是否错误?

这是 EF 对象:

[Table("DOCUMENTO")]
    public class Documento : EntidadeBase
    

        [Column("numero_documento", TypeName = "varchar")]
        [Display(Name = "Numero")]
      public string NumeroDocumento  get; set; 

// removed for clarity

这是桌子

CREATE TABLE [dbo].[DOCUMENTO](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [numero_documento] [varchar](50) NULL,
-- removed for clarity

【问题讨论】:

“参数被替换后”是什么意思?参数不会被替换,它们被用作变量。 @p__linq__0 参数的类型应为varcharnvarchar=@p__linq__0 等效于='47837'=N'47837',而不是=47837。该问题应该与参数嗅探或nvarcharvarchar 的转换有关。尝试在 DbContext 派生类构造函数中设置 this.Configuration.UseDatabaseNullSemantics = true; 【参考方案1】:

我怀疑这个实体/列周围的配置有些不同,或者可能是上下文导致了这个问题。我试图重现该问题,但没有遇到同样的问题:

我在我的一个测试表中添加了一个 varchar 列,并将数值输入它以供搜索。

使用与您所拥有的相同的演员:

Expression<Func<Course, bool>> where = (x => (string)x.SomeNumber.ToString() == (string)testId.ToString());

我从 EF 得到一条 SQL 语句:

exec sp_executesql N'SELECT 
    [Extent1].[CourseId] AS [CourseId], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[SomeNumber] AS [SomeNumber]
    FROM [dbo].[Courses] AS [Extent1]
    WHERE ((CASE WHEN ([Extent1].[SomeNumber] IS NULL) THEN N'''' ELSE [Extent1].[SomeNumber] END) = (CASE WHEN (@p__linq__0 IS NULL) THEN N'''' ELSE @p__linq__0 END)) OR ((CASE WHEN ([Extent1].[SomeNumber] IS NULL) THEN N'''' ELSE [Extent1].[SomeNumber] END IS NULL) AND (CASE WHEN (@p__linq__0 IS NULL) THEN N'''' ELSE @p__linq__0 END IS NULL))',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'12'
go

过滤器读取为 N'12'

如果我删除了不必要的强制转换(DB 列是 varchar(50) 并且使用的变量是字符串。)

Expression&lt;Func&lt;Course, bool&gt;&gt; where = (x =&gt; x.SomeNumber == testId);

结果:

exec sp_executesql N'SELECT 
    [Extent1].[CourseId] AS [CourseId], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[SomeNumber] AS [SomeNumber]
    FROM [dbo].[Courses] AS [Extent1]
    WHERE ([Extent1].[SomeNumber] = @p__linq__0) OR (([Extent1].[SomeNumber] IS NULL) AND (@p__linq__0 IS NULL))',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'12'
go

再次,N'12'

当我将列归因于 TypeName="varchar" 时,nvarchar(4000) 和 N'12' 变为 varchar(8000) 和 '12'。在第二个示例中,然而在第一个示例中(带有额外的转换)奇怪的是它仍然将参数称为 nvarchar。

您能否在构建表达式时尝试从项目中移除强制转换。如果需要转换值,请尝试仅转换值,而不是实体侧​​属性: IE。

Expression<Func<Documento, bool>> query = (t => t.NumeroDocumento == numeroOriginal);

Expression<Func<Documento, bool>> query = (t => t.NumeroDocumento == numeroOriginal.ToString()); // if numeroOriginal may not be a string.

除此之外,您使用的是哪个版本的实体框架?

【讨论】:

以上是关于实体框架从表达式生成错误的参数类型的主要内容,如果未能解决你的问题,请参考以下文章

首先通过将类型作为参数传递来动态实例化实体框架数据库中的模型对象

查询参数的值表达式包含错误;从类型 Object() 到类型“String”的转换无效

阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_2 请求参数绑定实体类型

使用swagger传递map类型的参数报错406

C# 根据类型名称 生成实体 调用方法

我有 3 个错误:预期为 ")" ,预期为表达式,long 类型的参数与 U32 类型的参数不兼容