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

10

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 bogue 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: Table définie par l'utilisateur insérant une mauvaise valeur

Je souhaite utiliser une table définie par l'utilisateur dans ma requête paramétrée, cette question explique comment procéder: Entity Framework Stored Procedure Table Value Parameter

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
)

EDIT:
Gert Arnold l'a compris. Sur la base de sa réponse, j'ai trouvé un rapport existant ici SQL Server Profiler TextData Column gère les entrées décimales de manière incorrecte

Joost K
la source
2
pouvez-vous essayer cela dataTable.Rows.Add(null,0.05m); et vérifier quelle requête il génère
rjs123431
1
@ rjs123431 J'ai déjà essayé ça et ça donne le même résultat
Joost K
1
Vous souhaitez créer une nouvelle table et renvoyer toutes les valeurs de la table? Désolé, mais je ne comprends pas ce que vous voulez vraiment. Pouvez-vous partager quel est votre objectif principal avec cela?
Lutti Coelho
1
@LuttiCoelho désolé pour la confusion, Select * from @ANamec'est comme espace réservé. Je me joins en fait à la table dans une plus grande requête que je ne pensais pas pertinente pour la question car cela reproduit déjà le problème dans un format plus simple.
Joost K
2
La chose étrange est, je vois en effet le SQL incorrect, mais quand j'utilise Database.SqlQuery(plutôt que Database.ExecuteSqlCommand) je reçois les valeurs correctes dans le client!
Gert Arnold

Réponses:

11

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

( MyContexest juste une classe héritée de DbContextrien d'autre)

Il n'y a qu'une seule valeur entre 0.001met 0.03m et c'est exactement ce que le rendement de 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.

Gert Arnold
la source
1
Je n'ai jamais pensé au problème de la journalisation. Grande pensée prête à l'emploi et merci d'avoir résolu ce problème!
Joost K du
2
Sur la base de votre réponse, j'ai reçu ce rapport de bogue feedback.azure.com/forums/908035-sql-server/suggestions/… Il semble que je n'étais pas le seul.
Joost K
1

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é la version EntityFramework 6.2.0 & 6.3.0 & 6.4.0 et aucune de celles-ci 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 l'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)

XAMT
la source
4
Je suis presque sûr que cela est lié aux paramètres de langue (machine vs base de données), donc vous avez juste de la chance.
Gert Arnold
2
Je dois ajouter que moi et @GertArnold vivons tous deux dans le même pays, ce qui explique très probablement pourquoi nous pouvons tous les deux reproduire le problème
Joost K
2
@GertArnold Veuillez faire un échantillon (VS Solution + SQL Database) et le partager. Je vais trouver l'indice.
XAMT
1
@XAMT Il s'agit d'un bogue Sql Server Profiler donc il est hors de nos mains. Si vous le souhaitez, vous pouvez jouer avec les paramètres de langue de votre machine et de votre serveur de base de données pour voir quand le bogue apparaît pendant que vous exécutez votre code, mais IMO est dans le département des passe-temps.
Gert Arnold