Tabella definita dall'utente in Entity Framework che genera query errata


10

Penso che sto attualmente riscontrando un bug in Entity Framework 6 e possibilmente ADO.NET. Dato che c'è una scadenza, non sono sicuro di poter aspettare che il bug venga risolto e spero che qualcuno possa aiutarmi a risolvere il problema.

Il problema è che la query utilizza i valori 1 e 5 nei punti in cui dovrebbe essere 0,01 e 0,05. Comunque stranamente 0.1 sembra funzionare

La query generata attualmente è: (ottenuta da 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

Mentre il codice corretto sarebbe:

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

Ho già creato un problema su github qui: tabella definita dall'utente che inserisce un valore errato

Voglio utilizzare una tabella definita dall'utente nella mia query con parametri, questa domanda spiega come eseguire questa operazione: Parametro valore tabella tabella stored procedure Entity Framework

Questo è il codice C # utilizzato per ottenere il codice SQL sopra

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 codice SQL per ottenere la tabella definita dall'utente

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

EDIT:
Gert Arnold l'ha capito. Sulla base della sua risposta, ho trovato un rapporto esistente qui. La colonna TextData di SQL Server Profiler gestisce gli input decimali in modo errato


2
puoi provare questo dataTable.Rows.Add(null,0.05m); e controllare quale query genera
rjs123431

1
@ rjs123431 L'ho provato prima e mi dà lo stesso risultato
Joost K

1
Vuoi creare una nuova tabella e restituire tutti i valori della tabella? Scusa, ma non capisco cosa vuoi davvero. Puoi condividere qual è il tuo obiettivo principale con questo?
Lutti Coelho,

1
@LuttiCoelho scusate la confusione, Select * from @ANameè come segnaposto. In realtà mi sto unendo al tavolo in una query più grande che non pensavo fosse rilevante per la domanda poiché questo già replica il problema in un formato più semplice.
Joost K

2
La cosa strana è che vedo davvero l'SQL errato, ma quando uso Database.SqlQuery(anziché Database.ExecuteSqlCommand) ricevo i valori corretti nel client!
Gert Arnold,

Risposte:


11

È uno strano artefatto di SQL Profiler. I valori vengono trasferiti correttamente. Posso dimostrarlo creando un database con il tipo definito dall'utente e una piccola tabella:

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 inserendo un paio di valori:

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

Quindi eseguo il tuo codice, leggermente adattato:

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è solo una classe che eredita DbContexte nient'altro)

C'è solo un valore compreso tra 0.001me 0.03m ed è esattamente quello che i rendimenti di query : 4.

Tuttavia, il profiler SQL Server registra questo:

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 in SSMS che restituisce il record n. 2.

Penso che abbia a che fare con impostazioni regionali e separatori decimali che si confondono con separatori di gruppi decimali da qualche parte nella registrazione.


1
Non ho mai pensato che il problema fosse nella registrazione. Fantastico fuori dagli schemi e grazie per aver risolto questo!
Joost K

2
Sulla base della tua risposta ho ricevuto questo bug report feedback.azure.com/forums/908035-sql-server/suggestions/… Sembra che non fossi il solo.
Joost K

1

Onestamente, non ho lo stesso problema come te:

Questo è il mio registro 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

Ho provato EntityFramework versione 6.2.0 e 6.3.0 e 6.4.0 e nessuno di questi mostra il 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());

Inoltre, collaudo ADO.NET e ho lo stesso risultato:

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

Sto usando Visual Studio 2017, .NET Framework 4.6.1 e Microsoft SQL Server Enterprise (64 bit)


4
Sono abbastanza sicuro che sia legato alle impostazioni della lingua (macchina vs database), quindi sei solo fortunato.
Gert Arnold,

2
Devo aggiungere che io e @GertArnold viviamo entrambi nello stesso paese, dando una spiegazione molto probabile sul perché entrambi possiamo riprodurre il problema
Joost K

2
@GertArnold Crea un campione (soluzione VS + database SQL) e condividilo. Troverò l'indizio.
XAMT

1
@XAMT È un bug di SQL Server Profiler, quindi è fuori dalle nostre mani. Se lo desideri, puoi giocare con le impostazioni della lingua della tua macchina e del server di database per vedere quando appare il bug mentre esegui il tuo codice, ma IMO è nel dipartimento del passatempo.
Gert Arnold,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.