Таблиця, визначена користувачем у Entity Framework, генеруючи неправильний запит


10

Я думаю, що в даний час я відчуваю помилку в Entity Framework 6 і, можливо, ADO.NET. Оскільки є термін, я не впевнений, що я можу дочекатися виправлення цієї помилки, і, сподіваюся, хтось може допомогти мені з чистою роботою.

Проблема полягає в тому, що запит використовує значення 1 і 5 в місцях, де він повинен бути 0,01 і 0,05. Однак досить дивно, здається, 0,1 працює

Наразі створений запит: (отримано від Profiler SQL Server)

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

Хоча правильним буде такий код:

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

Я вже створив проблему на github тут: таблиця, визначена користувачем, вставляючи неправильне значення

Я хочу використовувати таблицю, визначену користувачем, у моєму параметризованому запиті. Це питання пояснює, як це робиться: Параметр значення таблиці збережених процедур Entity Framework Параметр

Це код C #, який використовується для отримання вищевказаного коду SQL

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

І SQL-код для отримання визначеної користувачем таблиці

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

EDIT:
Герт Арнольд зрозумів це. На основі його відповіді я знайшов існуючий звіт тут Стовпець профайлера SQL Server TextData стовпець обробляє десяткові вводи неправильно


2
ви можете спробувати це dataTable.Rows.Add(null,0.05m); і перевірити, який запит він генерує
rjs123431

1
@ rjs123431 Я спробував це раніше, і це дає такий же результат
Joost K

1
Ви хочете створити нову таблицю і повернути всі значення таблиці? Вибачте, але я не розумію, чого ви насправді хочете. Чи можете ви поділитися, яка ваша основна мета з цим?
Лутті Коельо

1
@LuttiCoelho вибачте за плутанину, Select * from @ANameце як заповнювач. Я фактично приєднуюсь до столу в більшому запиті, який, на мою думку, не мав відношення до питання, оскільки це вже копіює проблему в більш простому форматі.
Joost K

2
Дивна річ у тому, що я дійсно бачу неправильний SQL, але коли я використовую Database.SqlQuery(а не Database.ExecuteSqlCommand), я отримую правильні значення в клієнті!
Герт Арнольд

Відповіді:


11

Це дивний артефакт Sql Profiler. Значення передаються правильно. Я можу продемонструвати це, створивши базу даних із визначеним вами типом та однією маленькою таблицею:

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

І вставляючи пару значень:

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

Потім я запускаю ваш код, трохи адаптований:

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це просто клас, що успадковує DbContextі нічого іншого)

Існує тільки одне значення між 0.001mі 0.03m і це саме те , що запит повертає : 4.

Однак, профілер Sql Server записує це:

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

І в SSMS, який повертає запис №2.

Я думаю, що це пов'язано з регіональними налаштуваннями та десятковими роздільниками, що змішуються з роздільниками десяткової групи десь у протоколі.


1
Я ніколи не замислювався над тим, щоб питання було в журналі. Чудово задумавшись і дякую за вирішення цього питання!
Joost K

2
На основі вашої відповіді я отримав цей звіт про помилку feedback.azure.com/forums/908035-sql-server/suggestions/… Здається, я був не єдиним.
Joost K

1

Чесно кажучи, у мене не така проблема, як у вас:

Це мій журнал профілів:

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

Я спробував EntityFramework версії 6.2.0 та 6.3.0 та 6.4.0, і жодне з них не показує проблеми:

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

Також я тестую ADO.NET і маю такий самий результат:

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

Я використовую Visual Studio 2017, .NET Framework 4.6.1 та Microsoft SQL Server Enterprise (64-розрядні)


4
Я впевнений, що це пов’язано з мовними налаштуваннями (машина проти бази даних), тому вам просто пощастило.
Герт Арнольд

2
Мушу додати, що я і @GertArnold обидва живуть в одній країні, і це дуже ймовірне пояснення, чому ми можемо відтворити проблему
Joost K

2
@GertArnold Будь ласка, зробіть зразок (VS Solution + SQL Database) та поділіться ним. Я знайду підказку.
XAMT

1
@XAMT Це помилка у програмі Sql Server Profiler, тому вона не в наших руках. Якщо вам подобається, ви можете грати з мовними настройками вашого машини та сервера баз даних, щоб побачити, коли з’являється помилка під час запуску коду, але IMO, який знаходиться у відділі проведення часу.
Герт Арнольд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.