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

10

Я думаю, что в настоящее время я испытываю ошибку в Entity Framework 6 и, возможно, ADO.NET. Поскольку существует крайний срок, я не уверен, что смогу дождаться исправления этой ошибки, и, надеюсь, кто-то может помочь мне с чистой работой вокруг.

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

Сгенерированный запрос в настоящее время: (полученный из 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

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

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
)

РЕДАКТИРОВАТЬ:
Герт Арнольд понял это. На основании его ответа я нашел здесь существующий отчет. Столбец SQL Server Profiler 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 profiler регистрирует это:

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-разрядная версия)

XAMT
источник
4
Я уверен, что это связано с настройками языка (машина против базы данных), так что вам просто повезло.
Герт Арнольд
2
Я должен добавить, что я и @GertArnold оба живем в одной стране, что очень вероятно объясняет, почему мы оба можем воспроизвести проблему
Joost K
2
@GertArnold Пожалуйста, сделайте образец (VS Solution + SQL Database) и поделитесь им. Я найду ключ.
XAMT
1
@XAMT Это ошибка Sql Server Profiler, поэтому она не в наших руках. Если вам нравится, вы можете поиграть с настройками языка вашего компьютера и сервера базы данных, чтобы увидеть, когда ошибка появляется, когда вы запускаете свой код, но IMO, который находится в отделе времяпрепровождения.
Герт Арнольд