Tabel yang ditentukan pengguna di Entity Framework menghasilkan kueri yang salah

10

Saya pikir saya sedang mengalami bug di Entity Framework 6 dan mungkin ADO.NET. Karena ada tenggat waktu, saya tidak yakin saya bisa menunggu bug ini diperbaiki dan mudah-mudahan seseorang dapat membantu saya dengan pekerjaan yang bersih.

Masalahnya adalah bahwa kueri menggunakan nilai 1 dan 5 di tempat yang seharusnya 0,01 dan 0,05. Namun anehnya 0,1 tampaknya berfungsi

Permintaan yang dihasilkan saat ini adalah: (didapat dari 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

Sedangkan kode yang benar adalah:

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

Saya sudah membuat masalah pada github di sini: Tabel yang ditentukan pengguna memasukkan nilai yang salah

Saya ingin menggunakan tabel yang ditentukan pengguna dalam kueri parameterisasi, pertanyaan ini menjelaskan bagaimana hal ini dilakukan: Kerangka Kerja Stored Prosedur Tabel Nilai Parameter

Ini adalah kode C # yang digunakan untuk mendapatkan kode SQL di atas

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

Dan kode SQL untuk mendapatkan tabel yang ditentukan pengguna

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

EDIT:
Gert Arnold menemukan jawabannya. Berdasarkan jawabannya, saya menemukan laporan yang ada di sini SQL Server Profiler TextData Kolom menangani Input Desimal secara tidak benar

Joost K
sumber
2
dapatkah Anda mencoba ini dataTable.Rows.Add(null,0.05m); dan memeriksa permintaan apa yang dihasilkannya
rjs123431
1
@ rjs123431 Saya mencoba itu sebelumnya dan itu memberikan hasil yang sama
Joost K
1
Anda ingin membuat tabel baru dan mengembalikan semua nilai tabel? Maaf, tapi saya tidak mengerti apa yang sebenarnya Anda inginkan. Bisakah Anda berbagi apa tujuan utama Anda dengan ini?
Lutti Coelho
1
@LuttiCoelho maaf atas kebingungan, Select * from @ANameini adalah sebagai placeholder. Saya benar-benar bergabung di atas meja dalam kueri yang lebih besar yang menurut saya tidak relevan dengan pertanyaan karena ini sudah mereplikasi masalah dalam format yang lebih sederhana.
Joost K
2
Yang aneh adalah, saya memang melihat SQL yang salah, tetapi ketika saya menggunakan Database.SqlQuery(bukannya Database.ExecuteSqlCommand) saya menerima nilai yang benar di klien!
Gert Arnold

Jawaban:

11

Ini adalah artefak Sql Profiler yang aneh. Nilai-nilai ditransfer dengan benar. Saya dapat menunjukkan bahwa dengan membuat database dengan tipe yang Anda tentukan dan satu tabel kecil:

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

Dan menyisipkan beberapa nilai:

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

Lalu saya jalankan kode Anda, sedikit disesuaikan:

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

( MyContexHanya kelas yang diwarisi dari DbContextdan tidak ada yang lain)

Hanya ada satu nilai antara 0.001mdan 0.03m dan itulah apa yang kembali permintaan : 4.

Namun, profiler Sql Server mencatat ini:

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

Dan di SSMS yang mengembalikan catatan # 2.

Saya pikir itu ada hubungannya dengan pengaturan regional dan pemisah desimal yang bercampur dengan pemisah kelompok desimal di suatu tempat di logging.

Gert Arnold
sumber
1
Saya tidak pernah berpikir tentang masalah penebangan. Luar biasa berpikir dan terima kasih telah memecahkan ini!
Joost K
2
Berdasarkan jawaban Anda, saya mendapat laporan bug ini feedback.azure.com/forums/908035-sql-server/suggestions/... Sepertinya saya bukan satu-satunya.
Joost K
1

Jujur, saya tidak memiliki masalah yang sama seperti Anda:

Ini adalah Log Profiler saya:

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

Saya mencoba EntityFramework versi 6.2.0 & 6.3.0 & 6.4.0 dan tidak ada yang menunjukkan masalah:

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

Juga, saya menguji ADO.NET dan memiliki hasil yang sama:

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

Saya menggunakan Visual Studio 2017, .NET Framework 4.6.1 dan Microsoft SQL Server Enterprise (64-bit)

XAMT
sumber
4
Saya cukup yakin ini terkait dengan pengaturan bahasa (mesin vs database), jadi Anda hanya beruntung.
Gert Arnold
2
Saya harus menambahkan bahwa saya dan @GertArnold keduanya tinggal di negara yang sama sehingga penjelasan yang sangat mungkin mengapa kita berdua dapat mereproduksi masalah
Joost K
2
@GertArnold Tolong buat sampel (VS Solution + SQL Database) dan bagikan. Saya akan menemukan petunjuknya.
XAMT
1
@XAMT Ini adalah bug Sql Server Profiler sehingga tidak ada di tangan kami. Jika suka, Anda dapat bermain dengan pengaturan bahasa mesin dan server database Anda untuk melihat kapan bug muncul saat Anda menjalankan kode Anda, tetapi IMO yang ada di departemen hobi.
Gert Arnold