简体   繁体   中英

Entity Framework generating wrong parameter type from expression

EF is creating a query that receives a int as parameter. it should be an varchar.

I have this C# code that generates an expression

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

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

Documento.Numero documento is a string. The corresponding filed in the database table is a varchar(50). numeroOriginal is also a string.

and this code for the obter (get) in the repository

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();
    }

When EF creates the sql query it creates this query(obtained from the debbuger)

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)

after the parameter is replaced this translates to

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

The correct query should be

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

Both work, but the second is much (and i mean MUCH) faster than the first. I ran both in SQL management studio. EF is getting the type of the parameter wrong? How do i fix this? Do I need to change the code or the database? Is my EF configuration for the table wrong?

This is the EF object:

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

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

// removed for clarity
}

and this is the table

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

I suspect there is something configured a bit different around this entity/column, or possibly the context that is tripping this up. I've tried to reproduce the issue, but I don't come across the same problem:

I added a varchar column to one of my test tables and fed it numeric values to search on.

With the casts same as what you have:

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

I get an SQL statement from EF:

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

the filter reads as N'12'

If I remove the unnecessary casts (the DB column is varchar(50) and the variable being used is a string.)

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

results in:

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

Again, the N'12'

When I attributed the column with TypeName="varchar" then the nvarchar(4000) and N'12' became varchar(8000) and '12'. in the second example, however in the first example (with the extra casting) curiously it still referred to the argument as an nvarchar.

Can you try removing the casting from your project when building the expression. If the value needs to be cast, try only casting the value, and not the entity-side property: Ie

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

or

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

Beyond this, what version of Entity Framework are you using?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM