2009-06-18 108 views
121

對於我沒有自由談,我們爲我們的SQL Server上定義2005數據庫,像這樣一個觀點幾種原因:實體框架和SQL Server視圖

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 

的想法是,實體框架將建立在此基礎上查詢,其中它的實體,但它指出以下錯誤產生的:

警告6002:表/視圖「Keystone_Local.dbo.MeterProvingStatisticsPoint」沒有一個主鍵定義。關鍵是推斷出來的,定義是作爲只讀表/視圖創建的。

並且它決定CompletedDateTime字段將是這個實體主鍵。

我們使用EdmGen生成模型。有沒有辦法不讓實體框架包含這個視圖的任何字段作爲主鍵?

回答

220

我們有同樣的問題,這是解決方案:

要強制實體框架使用的列作爲主鍵,使用ISNULL。

要強制實體框架不要使用列作爲主鍵,請使用NULLIF。

一個簡單的方法來應用這個是在另一個select中包裝你的視圖的select語句。

例子:

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

我認爲這是最好的希望。底線是有效的。 – MvcCmsJon 2010-11-13 19:41:51

+0

我試過這個,它不起作用。 EF設計師是否分析視圖定義或僅從數據結果推斷列? – sabanito 2011-11-09 18:37:49

+1

謝謝!它工作完美。 @sabanito我認爲它解析的定義。這就是爲什麼你需要特別包裝IsNull()中的關鍵屬性。我有一個不返回任何空值的視圖(並且不能返回任何空值),但由於寫入邏輯的方式,EF直到我將鍵包裝在IsNull()中時才能確定是否如此。 – Rabbi 2011-12-28 20:27:57

3
+0

這很有道理。那麼,有沒有辦法在我們定義它的方式中將列定義爲非null或null? – 2009-06-18 18:31:04

+1

對不起,我已經超出了我在Entity Framework的專業水平。 :-) – RBarryYoung 2009-06-18 20:07:51

+1

任何人都知道這個問題何時會被解決?當你擁有非主鍵的非空列時,不得不解決這個問題。 – 2011-08-11 15:45:04

4

當前實體框架電火花發生器將創建一個從您的視圖中的所有非可空領域的複合鍵。爲了獲得對此的控制,您將需要修改視圖和基礎表列,並在不希望它們成爲主鍵的一部分時將列設置爲可爲空。正如我遇到的情況,情況正好相反,EDM生成的密鑰導致數據重複問題,所以我必須將可空列定義爲非空值,以強制EDM中的組合鍵包含該列。

+0

我們與推測的PK有同樣的問題,實體返回重複的記錄並且完全令人討厭。如果您執行`Context.Entity.ToList()`重複記錄,但是如果直接執行由EF生成的SQL查詢(通過LINQPad獲取),則不會發生記錄重複。似乎是將數據庫記錄映射到返回的實體對象(PO​​CO)的問題,因爲使用解釋的邏輯(不可爲空的列)推斷PK。 – 2015-02-22 08:45:40

3

要得到一個視圖,我只能顯示一個主鍵列我創建了第二個視圖,指向第一個和使用NULLIF使類型爲空。這對我來說很有效,因爲EF認爲視圖中只有一個主鍵。

不知道這是否會幫助你,因爲我不相信EF會接受一個沒有主鍵的實體。

45

同意@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)在計劃中包含正確的索引。

如果

7

這種方法適用於我。我使用ISNULL()作爲主鍵字段,而COALESCE()如果字段不應該是主鍵,但也應該具有非空值。此示例使用不可爲空的主鍵生成ID字段。其他字段不是鍵,並且有(無)作爲它們的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 
58

我能夠使用設計器來解決這個問題。

  1. 打開模型瀏覽器。
  2. 查找圖中的視圖。
  3. 右鍵單擊主鍵,並確保選中「實體鍵」。
  4. 多選所有非主鍵。使用Ctrl或Shift鍵。
  5. 在屬性窗口中(如果需要查看它,請按F4鍵),將 「實體鍵」下拉列表更改爲False。
  6. 保存更改。
  7. 關閉Visual Studio並重新打開它。我正在使用Visual Studio 2013和EF 6 ,我必須這樣做才能讓警告消失。

我不必將我的視圖更改爲使用ISNULL,NULLIF或COALESCE解決方法。如果您從數據庫更新模型,則警告將重新出現,但如果您關閉並重新打開VS,將會消失。您在設計器中所做的更改將被保留,不會受到刷新的影響。

2

我還建議如果您不想混淆將ROW_NUMBER合併到您的選擇並將其設置爲主鍵並將所有其他列/成員設置爲非主要模型的主鍵。

1

由於上述問題,我更喜歡錶值函數。

如果你有這樣的:

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

創建此:

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

然後只需導入函數,而不是視圖。