Tabela zdefiniowana przez użytkownika w Entity Framework generująca niepoprawne zapytanie

10

Myślę, że obecnie mam błąd w Entity Framework 6 i prawdopodobnie ADO.NET. Ponieważ upłynął termin, nie jestem pewien, czy mogę poczekać na naprawienie tego błędu i mam nadzieję, że ktoś może mi pomóc w czystej pracy.

Problem polega na tym, że zapytanie używa wartości 1 i 5 w miejscach, w których powinno wynosić 0,01 i 0,05. Dziwnie jednak wydaje się, że 0,1 działa

Generowane zapytanie jest obecnie: (pochodzi z 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

Prawidłowy kod to:

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

Utworzyłem już problem na githubie: Tabela zdefiniowana przez użytkownika wstawia niepoprawną wartość

Chcę użyć tabeli zdefiniowanej przez użytkownika w moim sparametryzowanym zapytaniu, w tym pytaniu wyjaśniono, jak to zrobić: Entity Framework Procedura przechowywana Parametr Wartość parametru

Jest to kod C # używany do uzyskania powyższego kodu 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());

I kod SQL, aby uzyskać tabelę zdefiniowaną przez użytkownika

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

EDYCJA:
Gert Arnold to rozgryzł. Na podstawie jego odpowiedzi znalazłem istniejący raport. SQL Server Profiler TextData Kolumna nieprawidłowo obsługuje dane dziesiętne

Joost K.
źródło
2
możesz to wypróbować dataTable.Rows.Add(null,0.05m); i sprawdzić, jakie zapytanie generuje
rjs123431
1
@ rjs123431 Próbowałem tego wcześniej i daje ten sam rezultat
Joost K
1
Chcesz utworzyć nową tabelę i zwrócić wszystkie wartości tabeli? Przepraszam, ale nie rozumiem, czego naprawdę chcesz. Czy możesz podzielić się tym, co jest twoim głównym celem?
Lutti Coelho,
1
@LuttiCoelho przepraszam za zamieszanie, Select * from @ANamejest jak symbol zastępczy. W rzeczywistości dołączam do stołu w większym zapytaniu, które nie wydaje mi się istotne dla pytania, ponieważ już to replikuje problem w prostszym formacie.
Joost K,
2
Dziwne jest to, że rzeczywiście widzę niepoprawny SQL, ale kiedy używam Database.SqlQuery(zamiast Database.ExecuteSqlCommand) otrzymuję prawidłowe wartości w kliencie!
Gert Arnold

Odpowiedzi:

11

To dziwny artefakt Sql Profiler. Wartości są przesyłane poprawnie. Mogę to wykazać, tworząc bazę danych z typem zdefiniowanym przez użytkownika i jedną małą tabelą:

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

I wstawianie kilku wartości:

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

Następnie uruchamiam twój kod, lekko dostosowany:

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

( MyContexto tylko klasa dziedzicząca DbContexti nic więcej)

Jest tylko jedna wartość pomiędzy 0.001m a 0.03m i to właśnie powraca zapytania : 4.

Jednak profiler Sql Server rejestruje to:

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

I w SSMS, który zwraca rekord nr 2.

Myślę, że ma to związek z ustawieniami regionalnymi i separatorami dziesiętnymi pomieszanymi z separatorami grup dziesiętnych gdzieś w logowaniu.

Gert Arnold
źródło
1
Nigdy nie myślałem o problemie z logowaniem. Świetne, nieszablonowe myślenie i dziękuję za rozwiązanie tego problemu!
Joost K,
2
W oparciu o twoją odpowiedź otrzymałem ten raport o błędach feedback.azure.com/forums/908035-sql-server/suggestions/… Wygląda na to, że nie byłem jedyny.
Joost K,
1

Szczerze mówiąc, nie mam tego samego problemu co ty:

To jest mój dziennik Profiler:

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

Próbowałem EntityFramework w wersji 6.2.0 i 6.3.0 i 6.4.0 i żaden z nich nie pokazuje problemu:

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

Ponadto testuję ADO.NET i mam ten sam wynik:

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

Używam Visual Studio 2017, .NET Framework 4.6.1 i Microsoft SQL Server Enterprise (64-bit)

XAMT
źródło
4
Jestem pewien, że ma to związek z ustawieniami języka (maszyna kontra baza danych), więc masz szczęście.
Gert Arnold,
2
Muszę dodać, że ja i @GertArnold mieszkamy w tym samym kraju, co jest bardzo prawdopodobnym wyjaśnieniem, dlaczego oboje możemy odtworzyć problem
Joost K
2
@GertArnold Zrób próbkę (rozwiązanie VS + baza danych SQL) i udostępnij ją. Znajdę wskazówkę.
XAMT,
1
@XAMT Jest to błąd Siler Server Profiler, więc nie ma go w naszych rękach. Jeśli chcesz, możesz bawić się ustawieniami języka komputera i serwera bazy danych, aby zobaczyć, kiedy błąd pojawia się podczas uruchamiania kodu, ale IMO jest w dziale rozrywki.
Gert Arnold,