Tabela definida pelo usuário no Entity Framework gerando consulta incorreta

10

Acho que atualmente estou enfrentando um bug no Entity Framework 6 e possivelmente no ADO.NET. Como existe um prazo, não tenho certeza se posso esperar que esse bug seja corrigido e espero que alguém possa me ajudar com uma solução limpa.

O problema é que a consulta usa os valores 1 e 5 em locais onde deve ser 0,01 e 0,05. No entanto, estranhamente, 0,1 parece estar funcionando

A consulta gerada atualmente é: (obtida do SQL Server Profiler)

declare @p3  dbo.someUDT
insert into @p3 values(NULL,5)
insert into @p3 values(5,0.10)
insert into @p3 values(NULL,1)
insert into @p3 values(1,2)

exec sp_executesql N'Select * from @AName',N'@AName  [dbo].[someUDT] READONLY',@AName=@p3

Enquanto o código correto seria:

declare @p3  dbo.someUDT
insert into @p3 values(NULL,0.05)
insert into @p3 values(0.05,0.10)
insert into @p3 values(NULL,0.01)
insert into @p3 values(0.01,0.02)

exec sp_executesql N'Select * from @AName',N'@AName  [dbo].[someUDT] READONLY',@AName=@p3

Já criei um problema no github aqui: Tabela definida pelo usuário, inserindo valor errado

Desejo usar uma tabela definida pelo usuário em minha consulta parametrizada, esta pergunta explica como isso é feito: Entity Framework Stored Procedure Table Value Parameter

Este é o código C # usado para obter o código SQL acima

DataTable dataTable = new DataTable();
dataTable.Columns.Add("value1", typeof(decimal));
dataTable.Columns.Add("value2", typeof(decimal));

dataTable.Rows.Add(null,0.05m); 
dataTable.Rows.Add(0.05m,0.1m); 
dataTable.Rows.Add(null,0.01m); 
dataTable.Rows.Add(0.01m,0.02m); 
List<SqlParameter> Parameters = new List<SqlParameter>();

Parameters.Add(new SqlParameter("@AName", SqlDbType.Structured) { Value = dataTable , TypeName= "dbo.someUDT" });

dbContext.Database.ExecuteSqlCommand("Select * from @AName", Parameters.ToArray());

E código SQL para obter a tabela definida pelo usuário

CREATE TYPE [dbo].[someUDT] AS TABLE
(
   [value1] [decimal](16, 5) NULL,
   [value2] [decimal](16, 5) NULL
)

Edição:
Gert Arnold descobriu. Com base em sua resposta, encontrei um relatório existente aqui. A coluna TextData do SQL Server Profiler lida com entradas decimais incorretamente

Joost K
fonte
2
você pode tentar este dataTable.Rows.Add(null,0.05m); e verificar o que consultá-lo gera
rjs123431
11
@ rjs123431 Eu tentei isso antes e dá o mesmo resultado #
Joost K
11
Deseja criar uma nova tabela e retornar todos os valores da tabela? Desculpe, mas eu não entendo o que você realmente quer. Você pode compartilhar qual é seu principal objetivo com isso?
Lutti Coelho
11
@LuttiCoelho desculpe pela confusão, ela Select * from @ANameé um espaço reservado. Na verdade, estou ingressando na tabela em uma consulta maior que não achei relevante para a pergunta, pois isso já replica o problema em um formato mais simples.
Joost K
2
O estranho é que, de fato, vejo o SQL incorreto, mas quando uso Database.SqlQuery(em vez de Database.ExecuteSqlCommand) recebo os valores corretos no cliente!
Gert Arnold

Respostas:

11

É um artefato estranho do Sql Profiler. Os valores são transferidos corretamente. Eu posso demonstrar isso criando um banco de dados com seu tipo definido pelo usuário e uma pequena tabela:

CREATE TABLE [dbo].[Values](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Value] [decimal](16, 5) NOT NULL,
 CONSTRAINT [PK_Values] PRIMARY KEY CLUSTERED ([Id] ASC) ON [PRIMARY]
GO

E inserindo alguns valores:

Id          Value
----------- ---------------------------------------
1           10.00000
2           1.00000
3           0.10000
4           0.01000

Então eu executo seu código, um pouco adaptado:

DataTable dataTable = new DataTable();
dataTable.Columns.Add("value1", typeof(decimal));
dataTable.Columns.Add("value2", typeof(decimal));

dataTable.Rows.Add(0.001m, 0.03m);
List<SqlParameter> Parameters = new List<SqlParameter>();

Parameters.Add(new SqlParameter("@AName", SqlDbType.Structured) { Value = dataTable, TypeName = "dbo.someUDT" });

using(var context = new MyContext(connStr))
{
    var query = "Select v.Id from dbo.[Values] v, @AName a "
        + " where v.Value BETWEEN a.value1 AND a.value2";
    var result = context.Database.SqlQuery<int>(query, Parameters.ToArray());
}

( MyContexé apenas uma classe herdada DbContexte nada mais)

Há apenas um valor entre 0.001me 0.03m e isso é exatamente o que a consulta retorna : 4.

No entanto, o criador de perfil do Sql Server registra isso:

declare @p3 dbo.someUDT
insert into @p3 values(1,3) -- See here: the log is warped

exec sp_executesql N'Select v.Value from dbo.[Values] v, @AName a  where v.Value BETWEEN a.value1 AND a.value2',N'@AName [dbo].[someUDT] READONLY',@AName=@p3

E no SSMS que retorna o registro # 2.

Eu acho que tem a ver com configurações regionais e separadores decimais se misturando com separadores de grupos decimais em algum lugar do log.

Gert Arnold
fonte
11
Eu nunca pensei sobre o problema estar no log. Excelente pensamento imediato e obrigado por resolver isso!
Joost K
2
Com base na sua resposta, recebi este relatório de bug feedback.azure.com/forums/908035-sql-server/suggestions/… Parece que eu não era o único.
Joost K
1

Honestamente, não tenho o mesmo problema que você:

Este é o meu log do Profiler:

declare @p3 dbo.someUDT
insert into @p3 values(NULL,0.05)
insert into @p3 values(0.05,0.10)
insert into @p3 values(NULL,0.01)
insert into @p3 values(0.01,0.02)

exec sp_executesql N'Select * from @AName',N'@AName [dbo].[someUDT] READONLY',@AName=@p3

Eu tentei a versão 6.2.0 e 6.3.0 e 6.4.0 do EntityFramework e nenhuma delas mostra o problema:

DataTable dataTable = new DataTable();
dataTable.Columns.Add("value1", typeof(decimal));
dataTable.Columns.Add("value2", typeof(decimal));

dataTable.Rows.Add(null, 0.05);
dataTable.Rows.Add(0.05M, 0.1M);
dataTable.Rows.Add(null, 0.01);
dataTable.Rows.Add(0.01, 0.02);
List<SqlParameter> Parameters = new List<SqlParameter>();

Parameters.Add(new SqlParameter("@AName", SqlDbType.Structured) { Value = dataTable, TypeName = "dbo.someUDT" });

var dbContext = new test01Entities();
dbContext.Database.ExecuteSqlCommand("Select * from @AName", Parameters.ToArray());

Além disso, eu testo o ADO.NET e tenho o mesmo resultado:

SqlConnection cn = new SqlConnection("Data Source=(local);Initial Catalog=Test01;Integrated Security=true;");
using (var cmd = new SqlCommand("[foo]", cn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cn.Open();
    cmd.Parameters.AddWithValue("@param1", 0.02);
    cmd.Parameters.AddWithValue("@param2", 0.020);
    cmd.ExecuteNonQuery();
}

Estou usando o Visual Studio 2017, .NET Framework 4.6.1 e Microsoft SQL Server Enterprise (64 bits)

XAMT
fonte
4
Tenho certeza de que está relacionado às configurações de idioma (máquina versus banco de dados), então você tem sorte.
Gert Arnold
2
Tenho de acrescentar que eu e @GertArnold ambos vivem no mesmo país fazendo que uma explicação muito provável por isso que tanto pode reproduzir o problema
Joost K
2
@GertArnold Faça uma amostra (Solução VS + Banco de Dados SQL) e compartilhe-a. Eu vou encontrar a pista.
XAMT 08/12/19
11
@XAMT É um bug do Sql Server Profiler, portanto está fora de nossas mãos. Se você quiser, pode jogar com as configurações de idioma da sua máquina e do servidor de banco de dados para ver quando o erro aparece enquanto você executa o código, mas com o IMO que está no departamento de passatempo.
Gert Arnold