2009-02-16 42 views
0

我需要一些幫助來理解ADO.NET實體框架。ADO.NET實體框架生成意外的問題INSERT

我試圖用ADO.NET實體框架來表示和操縱WPF TreeView控件中的分層數據。

ADO.NET Entity Framework Object with parent and children http://img14.imageshack.us/img14/7158/thingpi1.gif

這些東西每個人都有一個單親家庭和零個或更多的孩子。

我的 「刪除」 按鈕...

 
Private Sub ButtonDeleteThing_Click(...) 
    db.DeleteObject(DirectCast(TreeViewThings.SelectedItem, Thing)) 
    db.SaveChanges() 
End Sub 

我監視SQL Server事件探查,同時調試我的應用程序:

  1. 的第一個按鈕點擊刪除就好了。
  2. 第二個按鈕單擊插入一個重複的父(但具有空的GUID uniqueidentifier主鍵),然後執行刪除。
  3. 第三個按鈕單擊失敗(違反PRIMARY KEY約束),因爲它無法用空的GUID主鍵插入另一行。

意外,產生的T-SQL複製...

 
exec sp_executesql N'insert [dbo].[Thing]([Id], [ParentId], ...) 
values (@0, @1, ...) ',N'@0 uniqueidentifier,@1 uniqueidentifier,...', 
@0='00000000-0000-0000-0000-000000000000', 
@1='389D987D-79B1-4A9D-970F-CE15F5E3E18A', 
... 

但是,它不只是刪除。我的「添加」按鈕具有與意外插入類似的行爲。它遵循相同的模式。

這讓我覺得有一個更基本的問題,我如何將這些實體類綁定到WPF TreeView或與我的數據模型本身。

下面是相關的代碼...

XAML ...

<TreeView Name="TreeViewThings" 
      ItemsSource="{Binding}" 
      TreeViewItem.Expanded="TreeViewThings_Expanded" 
      TreeViewItem.Selected="TreeViewThings_Selected" 
      ... > 
    <TreeView.Resources> 
     <HierarchicalDataTemplate DataType="{x:Type local:Thing}" 
            ItemsSource="{Binding Children}"> 
      <TextBlock Text="{Binding Path=Title}" /> 
     </HierarchicalDataTemplate> 
    </TreeView.Resources> 
</TreeView> 
<Button Name="ButtonAddThing" Content="Add Thing" ... /> 
<Button Name="ButtonDeleteThing" Content="Delete Thing" ... /> 

的Visual Basic ...

 
Partial Public Class Window1 

    Dim db As New ThingProjectEntities 

    Private Sub Window1_Loaded(...) Handles MyBase.Loaded 
     TreeViewThings.ItemsSource = _ 
      From t In db.Thing.Include("Children") _ 
      Where (t.Parent Is Nothing) _ 
      Select t 
    End Sub 

    Private Sub TreeViewThings_Expanded(...) 
     Dim ExpandedTreeViewItem As TreeViewItem = _ 
      DirectCast(e.OriginalSource, TreeViewItem) 
     LoadTreeViewChildren(ExpandedTreeViewItem) 
    End Sub 

    Sub LoadTreeViewChildren(ByRef Parent As TreeViewItem) 
     Dim ParentId As Guid = DirectCast(Parent.DataContext, Thing).Id 
     Dim ChildThings As System.Linq.IQueryable(Of Thing) 
     ChildThings = From t In db.Thing.Include("Children") _ 
         Where t.Parent.Id = ParentId _ 
         Select t 
     Parent.ItemsSource = ChildThings 
    End Sub 

    Private Sub ButtonAddThing_Click(...) 
     Dim NewThing As New Thing 
     NewThing.Id = Guid.NewGuid() 
     Dim ParentId As Guid = _ 
      DirectCast(TreeViewThings.SelectedItem, Thing).Id 
     NewThing.Parent = (From t In db.Thing _ 
          Where t.Id = ParentId _ 
          Select t).First 
     ... 
     db.AddToThing(NewThing) 
     db.SaveChanges() 
     TreeViewThings.UpdateLayout() 
    End Sub 

    Private Sub ButtonDeleteThing_Click(...) 
     db.DeleteObject(DirectCast(TreeViewThings.SelectedItem, Thing)) 
     db.SaveChanges() 
    End Sub 

    ... 

End Class 

我在做什麼錯?爲什麼它會產生這些奇怪的插入物?


更新:

我已經取得了突破性進展。但是,我仍然無法解釋它。

當我爲這個問題簡化我的代碼時,我擺脫了原因。

而不是使用LINQ如:

 
From t In db.Thing.Include("Children") Where ... 

我一直在使用LINQ如:

 
From t In db.Thing.Include("Children").Include("Brand") Where ... 

你看,我的事實體是另一品牌的實體。

ADO.NET Entity Framework Object with parent and children and a related Object http://img25.imageshack.us/img25/3268/thingbrandct4.gif

我認爲這是無關緊要的,所以我沒有在這個問題包括它上面。

顯然這是我的意外,問題插入我的事情表中的原因。

但是,爲什麼?任何人都可以解釋爲什麼會發生?我想更好地理解它。

回答

2

你爲什麼要添加新的子時,當你已經有對象重新載入家長的問題?雖然這對數據庫來說不成問題,但它會在對象級別引入不一致。您可以使用像這樣的現有父級:

Private Sub ButtonAddThing_Click(...) 
    Dim NewThing As New Thing 
    NewThing.Id = Guid.NewGuid() 
    Dim Parent As Thing = DirectCast(TreeViewThings.SelectedItem, Thing) 
    NewThing.Parent = Parent 
    ... 
    db.AddToThing(NewThing) 
    db.SaveChanges() 
    TreeViewThings.UpdateLayout() 
End Sub 

關於刪除:是否指定了級聯刪除數據庫中?

1

我還沒有詳細瞭解代碼,但首先要考慮的是,您不應該在層次結構的每個級別調用DeleteObject。與其他O/RM一樣的EF跟蹤對象及其關聯。

比方說,例如,您有父母 - >子女1 .. *的關係。如果查詢父對象,從父對象集合中移除一個子對象,然後調用SaveChanges(),EF將爲您生成相應的DELETE SQL語句 - 您無需自己跟蹤它。

所以,更好的方式來實現你的情況是這樣做:

  1. 查詢對象層次了EF的。
  2. 將它綁定到您的用戶界面。讓你的用戶界面修改你認爲合適的內存對象。
  3. 完成後,調用SaveChanges並讓EF找出該做什麼。

讓我知道是否有幫助。

+0

我一次只刪除一件事。 – 2009-02-20 13:34:03

+0

這就是「讓EF弄清楚做什麼」的一部分,這給我帶來麻煩。 – 2009-02-20 13:34:35

+0

要刪除一個子對象,從對象圖中刪除它就足夠了。同樣,要添加一個新對象,請將其添加到對象圖中。 EF正在跟蹤圖表,以便在調用SaveChanges時知道該怎麼做。也許可以嘗試在你的用戶界面之外編寫一個單元測試,以瞭解它是如何工作的。 – 2009-02-20 17:30:56

1

這裏有兩件事情。我不完全理解他們之間的關係,但我想我可以讓你走。

您需要了解的第一件事是實體框架不能很好地處理刪除不完全物化實例。這就是爲什麼包含改變你看到的行爲。因此,如果您有一個實體彙總了一系列子項,則需要在調用delete之前加載這些子項。只有當子實例在內存中時,它們纔會在父代之前被刪除。因此,無論是否包含,您都需要在調用Delete之前執行此類操作。

if (!thing.BrandReference.IsLoaded) thing.BrandReference.Load(); 

如果你叫上包含的關係,那麼這將做什麼,如果你沒有,那麼這將確保之前,你

的獨特理解的第二件事情是,一切都物化inserting a new entity with a relationship to an existing entity is conceptually two different inserts。這是由於關係在實體框架中是一流的。第一次插入是實體本身,第二次插入是關係。在這種情況下,關係沒有單獨的表,因此只需要一個實際的數據庫插入。但是,實體框架只有在映射正確的情況下才能確定。

那麼在這種情況下發生了什麼?接下來是基於我在這裏看到的一些事情的猜測。但我認爲情況比我所描述的更加複雜,所以我相信接下來的一些細節是不正確的。不過,它可能足夠接近以幫助您解決實際問題。

  1. 在試圖刪除它之前,您的實例未實現完全物化。
  2. 當您嘗試刪除其他內容時,框架試圖查看相同的關係。它發現了一些失敗的東西,並試圖將事情恢復到良好的狀態,但沒有成功。
  3. 然後,您試圖再次刪除,2次重複,只是這次由於數據庫限制,它甚至不太成功。
  4. 使用包括修復了1