Tabla definida por el usuario en Entity Framework que genera una consulta incorrecta

10

Creo que actualmente estoy experimentando un error en Entity Framework 6 y posiblemente en ADO.NET. Dado que hay una fecha límite, no estoy seguro de que pueda esperar a que se solucione este error y espero que alguien pueda ayudarme con una solución limpia.

El problema es que la consulta usa los valores 1 y 5 en lugares donde debería ser 0.01 y 0.05. Sin embargo, por extraño que parezca, 0.1 parece estar funcionando

La consulta generada actualmente es: (obtenida de 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

Mientras que el código correcto sería:

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

Ya creé un problema en github aquí: tabla definida por el usuario que inserta un valor incorrecto

Quiero usar una tabla definida por el usuario en mi consulta parametrizada, esta pregunta explica cómo se hace esto: Entity Framework Stored Procedure Table Value Parámetro

Este es el código C # utilizado para obtener el código SQL anterior

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

Y el código SQL para obtener la tabla definida por el usuario

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

EDITAR:
Gert Arnold lo descubrió. Según su respuesta, encontré un informe existente aquí. La columna de datos de datos del analizador de SQL Server maneja las entradas decimales incorrectamente

Joost K
fuente
2
¿puede probar esto dataTable.Rows.Add(null,0.05m); y verificar qué consulta genera
rjs123431
1
@ rjs123431 Lo intenté antes y da el mismo resultado
Joost K,
1
¿Desea crear una nueva tabla y devolver todos los valores de la tabla? Lo siento, pero no entiendo lo que realmente quieres. ¿Puedes compartir cuál es tu objetivo principal con esto?
Lutti Coelho
1
@LuttiCoelho perdón por la confusión, Select * from @ANamees como marcador de posición. De hecho, me estoy uniendo sobre la mesa en una consulta más grande que no pensé que fuera relevante para la pregunta, ya que esto ya replica el problema en un formato más simple.
Joost K
2
Lo extraño es que realmente veo el SQL incorrecto, pero cuando uso Database.SqlQuery(en lugar de Database.ExecuteSqlCommand) ¡recibo los valores correctos en el cliente!
Gert Arnold

Respuestas:

11

Es un extraño artefacto de Sql Profiler. Los valores se transfieren correctamente. Puedo demostrar eso creando una base de datos con su tipo definido por el usuario y una pequeña tabla:

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 insertando un par de valores:

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

Luego ejecuto su código, ligeramente 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());
}

( MyContexes solo una clase que hereda DbContexty nada más)

Solo hay un valor entre 0.001my0.03m y eso es exactamente lo que devuelve la consulta : 4.

Sin embargo, el perfilador de SQL Server registra esto:

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

Y en SSMS que devuelve el registro # 2.

Creo que tiene que ver con configuraciones regionales y separadores decimales que se mezclan con separadores de grupos decimales en algún lugar del registro.

Gert Arnold
fuente
1
Nunca pensé en el problema al iniciar sesión. ¡Genial pensar de inmediato y gracias por resolver esto!
Joost K
2
Según su respuesta, recibí este informe de error feedback.azure.com/forums/908035-sql-server/suggestions/… Parece que no fui el único.
Joost K
1

Honestamente, no tengo el mismo problema que tú:

Este es mi registro de perfil:

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

Intenté EntityFramework versión 6.2.0 y 6.3.0 y 6.4.0 y ninguno de estos muestra el 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());

Además, pruebo el ADO.NET y tengo el mismo 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();
}

Estoy usando Visual Studio 2017, .NET Framework 4.6.1 y Microsoft SQL Server Enterprise (64 bits)

XAMT
fuente
44
Estoy bastante seguro de que está relacionado con la configuración del idioma (máquina vs base de datos), por lo que tienes suerte.
Gert Arnold
2
Tengo que añadir que yo y @GertArnold Ambos viven en el mismo país que hace que una explicación muy probable por tanto podemos reproducir el problema
Joost K
2
@GertArnold Haga una muestra (Solución VS + Base de datos SQL) y compártala. Encontraré la pista.
XAMT
1
@XAMT Es un error de SQL Server Profiler, así que está fuera de nuestras manos. Si lo desea, puede jugar con la configuración de idioma de su máquina y el servidor de la base de datos para ver cuándo aparece el error mientras ejecuta su código, pero la OMI está en el departamento de pasatiempos.
Gert Arnold