Entity Framework และ SQL Server View


135

ด้วยเหตุผลหลายประการที่ฉันไม่มีเสรีภาพที่จะพูดถึงเรากำลังกำหนดมุมมองบนฐานข้อมูล Sql Server 2005 ของเราดังนี้:

CREATE VIEW [dbo].[MeterProvingStatisticsPoint]
AS
SELECT
    CAST(0 AS BIGINT) AS 'RowNumber',
    CAST(0 AS BIGINT) AS 'ProverTicketId',
    CAST(0 AS INT) AS 'ReportNumber',
    GETDATE() AS 'CompletedDateTime',
    CAST(1.1 AS float) AS 'MeterFactor',
    CAST(1.1 AS float) AS 'Density',
    CAST(1.1 AS float) AS 'FlowRate',
    CAST(1.1 AS float) AS 'Average',
    CAST(1.1 AS float) AS 'StandardDeviation',
    CAST(1.1 AS float) AS 'MeanPlus2XStandardDeviation',
    CAST(1.1 AS float) AS 'MeanMinus2XStandardDeviation'
WHERE 0 = 1

แนวคิดคือ Entity Framework จะสร้างเอนทิตีตามแบบสอบถามนี้ซึ่งทำ แต่สร้างขึ้นด้วยข้อผิดพลาดที่ระบุต่อไปนี้:

คำเตือน 6002: ตาราง / มุมมอง 'Keystone_Local.dbo.MeterProvingStatisticsPoint' ไม่มีคีย์หลักที่กำหนด คีย์ได้รับการอนุมานและคำจำกัดความถูกสร้างขึ้นเป็นตาราง / มุมมองแบบอ่านอย่างเดียว

และจะตัดสินใจว่าเขตข้อมูล CompletedDateTime จะเป็นคีย์หลักของเอนทิตีนี้

เรากำลังใช้ EdmGen เพื่อสร้างแบบจำลอง มีวิธีที่จะไม่ให้กรอบงานเอนทิตีรวมฟิลด์ใด ๆ ของมุมมองนี้เป็นคีย์หลักหรือไม่?

คำตอบ:


247

เรามีปัญหาเดียวกันและนี่คือวิธีแก้ปัญหา:

ในการบังคับให้กรอบงานเอนทิตีใช้คอลัมน์เป็นคีย์หลักให้ใช้ ISNULL

ในการบังคับให้กรอบงานเอนทิตีไม่ใช้คอลัมน์เป็นคีย์หลักให้ใช้ NULLIF

วิธีง่ายๆในการนำไปใช้คือการรวมคำสั่ง select ของมุมมองของคุณไว้ใน Select อื่น

ตัวอย่าง:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp

2
ฉันคิดว่านี่เป็นสิ่งที่ดีที่สุดที่จะหวัง บรรทัดล่างได้ผล
MvcCmsJon

1
ขอบคุณ! มันทำงานได้อย่างสมบูรณ์แบบ @sabanito ฉันคิดว่ามันแยกวิเคราะห์คำจำกัดความ นั่นคือเหตุผลที่คุณต้องรวมคุณสมบัติหลักใน IsNull () โดยเฉพาะ ฉันมีมุมมองที่ไม่คืนค่า null ใด ๆ (และไม่สามารถคืนค่า null ใด ๆ ) แต่เนื่องจากวิธีการเขียนตรรกะ EF ไม่สามารถระบุได้ว่าเป็นเช่นนั้นจนกว่าฉันจะรวมคีย์ใน IsNull ()
รับบี

3
ปัญหาเดียวที่ฉันเห็นที่นี่คือมุมมองนั้นอาจจำเป็นต้องส่งคืนสตริงว่าง '' สิ่งที่ฉันทำก็เพียงแค่ส่งคอลัมน์กลับไปเป็นประเภทข้อมูลของตัวเอง ตัวอย่างเช่นถ้า AnotherProperty มีประเภทข้อมูลเป็น varchar (50) ฉันจะเหวี่ยงมันเป็น 'CONVERT (VARCHAR (50), AnotherProperty) AS [AnotherProperty]' สิ่งนี้ปิดบังความว่างเปล่าจาก EF และอนุญาตให้ใช้สตริงว่าง
บาร์ต

2
ใช่วิธีนี้ใช้ได้เช่นทำให้ EF ใช้คอลัมน์เป็นคีย์หลัก isnull (CONVERT (VARCHAR (50), newid ()), '') AS [PK]
dc2009

2
นอกเหนือจากการมีข้อความที่น่ารำคาญในโซลูชันแล้วยังมีอันตรายใด ๆ ที่จะไม่แก้ไขปัญหานี้หรือไม่ ฉันเห็นด้วยกับวิธีแก้ปัญหาของคุณ แต่ตรงไปตรงมาฉันไม่รู้สึกว่าฉันควรจะต้องทำสิ่งนี้ - ฉันคิดว่าเราทุกคนสามารถตกลงได้ว่านี่เป็นข้อบกพร่องใช่ไหม
dyslexicanaboko

68

ฉันสามารถแก้ไขปัญหานี้ได้โดยใช้นักออกแบบ

  1. เปิด Model Browser
  2. ค้นหามุมมองในแผนภาพ
  3. คลิกขวาที่คีย์หลักและตรวจสอบว่าได้เลือก "คีย์เอนทิตี" แล้ว
  4. เลือกคีย์ที่ไม่ใช่คีย์หลักทั้งหมด ใช้แป้น Ctrl หรือ Shift
  5. ในหน้าต่าง Properties (กด F4 หากจำเป็นเพื่อดู) ให้เปลี่ยนเมนูแบบเลื่อนลง "Entity Key" เป็น False
  6. บันทึกการเปลี่ยนแปลง.
  7. ปิด Visual Studio แล้วเปิดใหม่ ฉันใช้ Visual Studio 2013 กับ EF 6 และต้องทำสิ่งนี้เพื่อให้คำเตือนหายไป

ฉันไม่จำเป็นต้องเปลี่ยนมุมมองเพื่อใช้วิธีแก้ปัญหา ISNULL, NULLIF หรือ COALESCE หากคุณอัปเดตโมเดลจากฐานข้อมูลคำเตือนจะปรากฏขึ้นอีกครั้ง แต่จะหายไปหากคุณปิดและเปิด VS ใหม่ การเปลี่ยนแปลงที่คุณทำในตัวออกแบบจะถูกเก็บรักษาไว้และไม่ได้รับผลกระทบจากการรีเฟรช


9
ได้รับการยืนยัน ต้องรีสตาร์ท VS2013 เพื่อให้คำเตือนหายไป
Michael Logutov

5
"คุณลองปิดแล้วเปิดใหม่อีกครั้งไหม" ;-) ขอบคุณทำงานได้อย่างมีเสน่ห์!
Obl Tobl

4
เมื่อฉันสร้างมุมมองพวกเขาจะไม่อยู่ในแผนภาพแบบจำลองด้วยซ้ำ พวกเขาแสดงความคิดเห็นในไฟล์ xml
ggderas

วิธีแก้ปัญหาที่ง่ายแสนง่ายและดูเหมือนจะไม่ใช่การแก้ไขแบบแฮ็กกี้มากเท่ากับการจัดการมุมมอง! ขอบคุณ.
LuqJensen

2
VS2017 ที่ยืนยันแล้วจำเป็นต้องรีสตาร์ทด้วยเพื่อให้คำเตือนหายไป
Marc Levesque

46

เห็นด้วยกับ @Tillito อย่างไรก็ตามในกรณีส่วนใหญ่มันจะทำให้ตัวเพิ่มประสิทธิภาพ SQL ผิดปกติและจะไม่ใช้ดัชนีที่ถูกต้อง

อาจเห็นได้ชัดสำหรับใครบางคน แต่ฉันใช้เวลาหลายชั่วโมงในการแก้ปัญหาด้านประสิทธิภาพโดยใช้โซลูชัน Tillito บอกว่าคุณมีโต๊ะ:

 Create table OrderDetail
    (  
       Id int primary key,
       CustomerId int references Customer(Id),
       Amount decimal default(0)
    );
 Create index ix_customer on OrderDetail(CustomerId);

และมุมมองของคุณเป็นแบบนี้

 Create view CustomerView
    As
      Select 
          IsNull(CustomerId, -1) as CustomerId, -- forcing EF to use it as key
          Sum(Amount) as Amount
      From OrderDetail
      Group by CustomerId

เครื่องมือเพิ่มประสิทธิภาพ Sql จะไม่ใช้ดัชนี ix_customer และจะทำการสแกนตารางในดัชนีหลัก แต่ถ้าแทนที่จะเป็น:

Group by CustomerId

คุณใช้

Group by IsNull(CustomerId, -1)

จะทำให้ MS SQL (อย่างน้อยปี 2008) รวมดัชนีที่ถูกต้องไว้ในแผน

ถ้า


2
นี่ควรเป็นความคิดเห็นเกี่ยวกับคำตอบของ Tillito ไม่ใช่คำตอบเองเนื่องจากไม่ได้ให้คำตอบสำหรับคำถามของ OP
zimdanen

6
ผู้ชายคนนี้มีตัวแทน 1 แต่ยังเพิ่มความคิดเห็นไม่ได้
jrcs3

@zimdanen ไม่มีทางที่คุณจะใส่ข้อมูลทั้งหมดนี้ลงในความคิดเห็นได้มันสมเหตุสมผลกว่าที่จะมีคำตอบแยกต่างหาก
Contango

2
@Contango: คำตอบนี้ได้รับการแก้ไขหกวันหลังจากโพสต์และฉันโพสต์ความคิดเห็นของฉัน ดูประวัติการแก้ไข
zimdanen

9

วิธีนี้ใช้ได้ดีกับฉัน ฉันใช้ ISNULL () สำหรับฟิลด์คีย์หลักและ COALESCE () ถ้าฟิลด์นั้นไม่ควรเป็นคีย์หลัก แต่ควรมีค่าที่ไม่เป็นค่าว่างด้วย ตัวอย่างนี้ให้ฟิลด์ ID ที่มีคีย์หลักที่ไม่เป็นค่าว่าง ฟิลด์อื่น ๆ ไม่ใช่คีย์และมี (None) เป็นแอตทริบิวต์ Nullable

SELECT      
ISNULL(P.ID, - 1) AS ID,  
COALESCE (P.PurchaseAgent, U.[User Nickname]) AS PurchaseAgent,  
COALESCE (P.PurchaseAuthority, 0) AS PurchaseAuthority,  
COALESCE (P.AgencyCode, '') AS AgencyCode,  
COALESCE (P.UserID, U.ID) AS UserID,  
COALESCE (P.AssignPOs, 'false') AS AssignPOs,  
COALESCE (P.AuthString, '') AS AuthString,  
COALESCE (P.AssignVendors, 'false') AS AssignVendors 
FROM Users AS U  
INNER JOIN Users AS AU ON U.Login = AU.UserName  
LEFT OUTER JOIN PurchaseAgents AS P ON U.ID = P.UserID

หากคุณไม่มีคีย์หลักจริงๆคุณสามารถปลอมแปลงได้โดยใช้ ROW_NUMBER เพื่อสร้างคีย์เทียมที่โค้ดของคุณละเว้น ตัวอย่างเช่น:

SELECT
ROW_NUMBER() OVER(ORDER BY A,B) AS Id,
A, B
FROM SOMETABLE

ใช่ฉันจบลงด้วยการโกงNEWID() as idแต่มันก็เป็นความคิดเดียวกัน และมีกรณีการใช้งานที่ถูกต้องตัวอย่างเช่นหากคุณมีมุมมองแบบอ่านอย่างเดียว น่าเกลียด EF น่าเกลียด
ruffin

4

ตัวสร้าง Entity Framework EDM ปัจจุบันจะสร้างคีย์ผสมจากฟิลด์ที่ไม่เป็นค่าว่างทั้งหมดในมุมมองของคุณ เพื่อให้สามารถควบคุมสิ่งนี้ได้คุณจะต้องปรับเปลี่ยนมุมมองและคอลัมน์ตารางที่อยู่เบื้องหลังการตั้งค่าคอลัมน์ให้เป็นโมฆะเมื่อคุณไม่ต้องการให้คอลัมน์เหล่านั้นเป็นส่วนหนึ่งของคีย์หลัก สิ่งที่ตรงกันข้ามก็เป็นจริงเช่นเดียวกับที่ฉันพบคีย์ที่สร้างโดย EDM ทำให้เกิดปัญหาการทำสำเนาข้อมูลดังนั้นฉันจึงต้องกำหนดคอลัมน์ที่เป็นโมฆะเป็นค่าว่างไม่ได้เพื่อบังคับให้คีย์คอมโพสิตใน EDM รวมคอลัมน์นั้น


เรามีปัญหาเดียวกันกับ PK ที่อนุมานเอนทิตีส่งคืนระเบียนที่ซ้ำกันและน่ารำคาญ หากคุณรันContext.Entity.ToList()เร็กคอร์ดที่ซ้ำกัน แต่ถ้าคุณรัน SQL Query ที่สร้างโดย EF โดยตรง (ได้มาจาก LINQPad) จะไม่มีการทำซ้ำเรกคอร์ด ดูเหมือนว่าจะเป็นปัญหาในการแมประเบียนฐานข้อมูลกับเอนทิตีอ็อบเจ็กต์ (POCO) ที่ส่งคืนเนื่องจาก PK ถูกอนุมานโดยใช้ตรรกะที่อธิบาย (คอลัมน์ที่ไม่เป็นโมฆะ)
David Oliván Ubieto

3

ดูเหมือนว่าเป็นปัญหาที่ทราบแล้วกับ EdmGen: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/


ที่สมเหตุสมผล ดังนั้นมีวิธีกำหนดคอลัมน์ว่าไม่ใช่โมฆะหรือโมฆะในมุมมองแบบที่เรากำหนดหรือไม่?
Sergio Romero

1
ขออภัยฉันเกินระดับความเชี่ยวชาญใน Entity Framework แล้ว :-)
RBarryYoung

1
มีใครทราบบ้างว่าปัญหานี้จะได้รับการแก้ไขเมื่อใด น่ารำคาญที่ต้องแก้ปัญหานี้เมื่อคุณมีคอลัมน์ที่ไม่ใช่ค่าว่างที่ไม่ใช่คีย์หลัก
live-love

3

ในการรับมุมมองฉันต้องแสดงคอลัมน์คีย์หลักเพียงคอลัมน์เดียวฉันสร้างมุมมองที่สองซึ่งชี้ไปที่รายการแรกและใช้ NULLIF เพื่อทำให้ประเภทเป็นโมฆะ สิ่งนี้ได้ผลสำหรับฉันในการทำให้ EF คิดว่ามีคีย์หลักเพียงตัวเดียวในมุมมอง

ไม่แน่ใจว่าสิ่งนี้จะช่วยคุณได้หรือไม่เนื่องจากฉันไม่เชื่อว่า EF จะยอมรับเอนทิตีที่ไม่มีคีย์หลัก


3

หากคุณไม่ต้องการยุ่งกับสิ่งที่ควรเป็นคีย์หลักขอแนะนำ:

  1. รวมROW_NUMBERเข้ากับสิ่งที่คุณเลือก
  2. ตั้งเป็นคีย์หลัก
  3. ตั้งค่าคอลัมน์ / สมาชิกอื่นทั้งหมดเป็นไม่ใช่หลักในโมเดล

1

เนื่องจากปัญหาดังกล่าวข้างต้นฉันชอบฟังก์ชันค่าตารางมากกว่า

หากคุณมีสิ่งนี้:

CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something

สร้างสิ่งนี้:

CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView])

จากนั้นคุณเพียงแค่นำเข้าฟังก์ชันแทนที่จะเป็นมุมมอง


2
คุณจะสร้างความเชื่อมโยงระหว่างหน่วยงานที่ดำเนินการตามแนวทางนี้ได้อย่างไร เป็นไปได้ไหม?
ggderas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.