2011-12-07 46 views
2

我有一個實體框架4設計,允許在不修改指向它們的實體的情況下刪除引用表(無級聯刪除)。因此,例如實體A在ID字段中具有對實體B的外鍵引用。 B可以被刪除(並且數據庫中沒有FK約束來阻止),所以如果我查看ABID,它總是一個有效的字段(因爲所有這些都會返回A中的ID字段),即使沒有由於前一次刪除而使用該ID記錄B.這是通過設計,我不想級聯刪除,我需要A記錄堅持一段時間用於審計目的。EF4:篩選出不存在的引用實體

問題是過濾不存在​​的已刪除記錄並不像聽起來那麼容易。因此,舉例來說,如果我這樣做:

from c in A 
select A.B.somefield; 

這導致OUTER JOIN在生成的SQL,所以它拿起,即使他們是指缺少乙記錄的所有A記錄。所以,我一直用來解決這個問題(因爲我找不到更好的方法!)是添加一個where子句來檢查引用的B記錄中的字符串字段。如果B實體中的該字段爲空,那麼我假設B不存在。

from c in A 
where c.B.somestringfield != null 
select A.B.somefield; 

似乎工作如果B.somestringfield是一個字符串。如果它是一個整數,這是行不通的!

這對我來說都是這樣。我想到了一些解決方案,但它們並不實用:

  1. 查詢刪除B時刪除B並刪除其外鍵的所有引用B的表。這非常難看,如果我在將來添加另一個引用B的實體,我不想記住這麼做。更何況一個巨大的性能延遲解決所有的引用,每當我刪除的東西。
  2. 將字符串字段添加到每個表,我可以指望在那裏,我可以檢查實體是否存在。 Blech,我不想爲此添加數據庫字段。
  3. 實施軟刪除並保持所有參考完整性 - 基本上設置了級聯刪除,但是這將導致巨大的數據庫膨脹,因爲由於引用而無法清除大量記錄。不行。

我原本以爲這個問題與舔了舔「檢查中引用的實體某個字段爲空」的把戲,但我不完全理解的條件(如果我沒有什麼任何條件下打破?在引用表什麼樣的領域將工作整數不會)

舉個例子,如果我在實體B的整場「數」和我檢查,看它是否爲null,如:?

from c in A 
where c.B.count != null 
select c.B.count; 

我得到一堆記錄與計數混合在結果爲空的記錄,事實上查詢炸彈與一個「InvalidOperationException:由於物化值爲null,因此值類型轉換爲'Int32'失敗。無論是結果型的泛型參數或查詢必須使用可空類型。」

所以我需要做的

from c in A 
where c.B.count != null 
select new { count = (int?)c.B.count }; 

,甚至看空的記錄。所以,這是非常令人費解對我怎麼說查詢可以結果完全沒有結果。

我剛剛發現的東西,如果我做一個明確加入這樣的,SQL是INNER JOIN,一切的偉大工程:

from c in A 
join j in B on A.B.ID equals j.ID 
select c; 

但這很爛。我將不得不修改大量查詢來添加顯式連接子句,而不是享受與EF獲得的關係字段的便利性。有點擊敗了目的,並增加了更多的代碼維護。

回答

1

當你說你的第一個代碼段創建了一個OUTER JOIN時,情況就是這樣,因爲B是實體A的可選導航屬性。對於所需的導航屬性,EF將創建一個INNER JOIN(在此處更詳細地解釋:https://stackoverflow.com/a/7640489/270591)。

因此,除了使用直接的SQL之外,我看到最後一個代碼片段(在LINQ中使用顯式join)的唯一選擇是使您的導航屬性爲必需。

在我看來,這仍然是一個非常醜陋的黑客行爲,在其他情況下可能會出現意想不到的行爲。如果需要導航屬性或可選EF爲此關係添加「語義含義」,即:如果存在外鍵!= NULL,則必須爲爲相關實體,並且EF期望您沒有移除強制執行數據庫中的FK約束。

+0

謝謝你,這是非常有用的信息。我沒有真正理解可選與必需的導航屬性,但它確實有意義,如果它只是映射到FK約束。在我的情況下,我完全同意我只是憎恨破壞必要的語義意義,因爲它不在設計之中,如果它引起可能出人意料的怪異行爲,就忘記它。在我看來,最好的方式是使用顯式的LINQ連接來修改某些查詢,以強制生成INNER JOIN並在這些情況下獲得正確的結果。謝謝! –