ตารางที่ผู้ใช้กำหนดใน 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 Stored Procedure Stored Procedure พารามิเตอร์ค่าพารามิเตอร์ของตาราง

นี่คือรหัส 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
)

แก้ไข:
Gert Arnoldคิดออก จากคำตอบของเขาฉันพบรายงานที่มีอยู่ที่นี่คอลัมน์ SQL Server Profiler TextData จัดการกับอินพุตทศนิยมไม่ถูกต้อง


2
คุณสามารถลองสิ่งนี้dataTable.Rows.Add(null,0.05m); และตรวจสอบสิ่งที่มันสร้างแบบสอบถาม
rjs123431

1
@ rjs123431 ฉันลองมาก่อนแล้วและให้ผลลัพธ์เหมือนกัน
Joost K

1
คุณต้องการสร้างตารางใหม่และคืนค่าทั้งหมดของตารางหรือไม่ ขออภัยฉันไม่เข้าใจสิ่งที่คุณต้องการจริงๆ คุณสามารถแบ่งปันเป้าหมายหลักของคุณกับสิ่งนี้ได้อย่างไร
Lutti Coelho

1
@ LuttiCoelho ขออภัยในความสับสนที่Select * from @ANameเป็นตัวยึด จริง ๆ แล้วฉันกำลังเข้าร่วมในตารางในแบบสอบถามที่ใหญ่กว่าที่ฉันไม่คิดว่าเกี่ยวข้องกับคำถามเนื่องจากสิ่งนี้ได้จำลองปัญหาในรูปแบบที่เรียบง่ายขึ้นแล้ว
Joost K

2
สิ่งที่แปลกคือฉันเห็น SQL ที่ไม่ถูกต้อง แต่เมื่อฉันใช้Database.SqlQuery(มากกว่าDatabase.ExecuteSqlCommand) ฉันจะได้รับค่าที่ถูกต้องในไคลเอนต์!
Gert Arnold

คำตอบ:


11

มันเป็นสิ่งประดิษฐ์ Profiler SQL ค่าจะถูกถ่ายโอนอย่างถูกต้อง ฉันสามารถแสดงให้เห็นว่าโดยการสร้างฐานข้อมูลด้วยประเภทที่ผู้ใช้กำหนดและตารางเล็ก ๆ น้อย ๆ ของคุณ:

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 บันทึกสิ่งนี้:

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

สุจริตฉันไม่ได้มีปัญหาเช่นเดียวกับคุณ:

นี่คือบันทึก 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

ฉันลอง 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
ฉันค่อนข้างแน่ใจว่ามันเกี่ยวข้องกับการตั้งค่าภาษา (ฐานข้อมูลเครื่องเทียบกับ) ดังนั้นคุณโชคดีมาก
Gert Arnold

2
ฉันต้องเพิ่มว่าฉันและ @GertArnold ทั้งคู่อาศัยอยู่ในประเทศเดียวกันทำให้เป็นคำอธิบายที่เป็นไปได้มากว่าทำไมเราทั้งคู่จึงสามารถสร้างปัญหาขึ้นอีกครั้ง
Joost K

2
@GertArnold โปรดสร้างตัวอย่าง (VS Solution + ฐานข้อมูล SQL) และแบ่งปัน ฉันจะพบเบาะแส
XAMT

1
@XAMT มันเป็นข้อผิดพลาด Profiler ของเซิร์ฟเวอร์ SQL ดังนั้นมันจึงอยู่ในมือของเรา ถ้าคุณชอบคุณสามารถเล่นกับการตั้งค่าภาษาของเครื่องและเซิร์ฟเวอร์ฐานข้อมูลของคุณเพื่อดูว่าข้อผิดพลาดปรากฏขึ้นในขณะที่คุณเรียกใช้รหัสของคุณ แต่ IMO ที่อยู่ในแผนกงานอดิเรก
Gert Arnold
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.