web-dev-qa-db-fra.com

Table définie par l'utilisateur dans Entity Framework générant une requête incorrecte

Je pense que je rencontre actuellement un bogue dans Entity Framework 6 et peut-être ADO.NET. Puisqu'il y a une date limite, je ne suis pas sûr de pouvoir attendre que ce bug soit corrigé et j'espère que quelqu'un pourra m'aider à faire le ménage.

Le problème est que la requête utilise les valeurs 1 et 5 aux endroits où elles devraient être 0,01 et 0,05. Cependant, étrangement, 0,1 semble fonctionner

La requête générée est actuellement: (obtenue à partir 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

Alors que le bon code serait:

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'ai déjà créé un problème sur github ici: Tableau défini par l'utilisateur insérant une mauvaise valeur

Je veux utiliser une table définie par l'utilisateur dans ma requête paramétrée, cette question explique comment cela est fait: Paramètre de valeur de table de procédure stockée Framework d'entité

Ceci est le code C # utilisé pour obtenir le code SQL ci-dessus

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

Et du code SQL pour obtenir la table définie par l'utilisateur

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

ÉDITER:
Gert Arnold l'a compris. Sur la base de sa réponse, j'ai trouvé un rapport existant ici la colonne TextData Profiler TextData ne gère pas correctement les entrées décimales

10
Joost K

C'est un étrange artefact Sql Profiler. Les valeurs sont transférées correctement. Je peux démontrer qu'en créant une base de données avec votre type défini par l'utilisateur et une petite table:

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

Et en insérant quelques valeurs:

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

Ensuite, je lance votre code, légèrement adapté:

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 est juste une classe héritant de DbContext et rien d'autre)

Il n'y a qu'une seule valeur entre 0.001m et 0.03met c'est exactement ce que renvoie la requête: 4.

Cependant, Sql Server profiler enregistre ceci:

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

Et dans SSMS qui renvoie l'enregistrement n ° 2.

Je pense que cela a à voir avec les paramètres régionaux et les séparateurs décimaux mélangés avec des séparateurs de groupes décimaux quelque part dans la journalisation.

11
Gert Arnold

Honnêtement, je n'ai pas le même problème que vous:

Voici mon journal de profileur:

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'ai essayé EntityFramework version 6.2.0 & 6.3.0 & 6.4.0 et aucun de ces derniers ne montre le problème:

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

Aussi, je teste ADO.NET et ai le même résultat:

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

J'utilise Visual Studio 2017, .NET Framework 4.6.1 et Microsoft SQL Server Enterprise (64 bits)

1
XAMT