R – ADO.NET Entity Framework generates unexpected, problem INSERTs

ado.net-entity-data-modelentity-frameworklinqnetwpf

I need some help understanding the ADO.NET Entity Framework.

I'm trying to represent and manipulate hierarchical data in a WPF TreeView control with the ADO.NET Entity Framework.

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

Each of these Things has a single parent and zero or more children.

My "delete" button…

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

I'm monitoring the SQL Server Profiler while I debug my application:

  1. The first button click deletes just fine.
  2. The second button click inserts a duplicate parent (but with empty GUID uniqueidentifier primary key) and then performs the delete.
  3. The third button click fails (Violation of PRIMARY KEY constraint) because it cannot insert another row with an empty GUID primary key.

The unexpected, generated T-SQL duplication…

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',
...

But, it's not just deletes. My "add" button has similar behavior with unexpected inserts. It follows the same pattern.

This makes me think there's a more fundamental problem with how I'm binding these entity classes to the WPF TreeView or with my data model itself.

Here's the relevant code…

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

What am I doing wrong? Why is it generating these strange inserts?


Update:

I've made a breakthrough. But, I still can't explain it.

I'd gotten rid of the cause when I simplified my code for this question.

Rather than using Linq such as:

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

I've been using Linq such as:

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

You see, My Thing entity is related to another Brand entity.

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

I thought it was irrelevant, so I didn't include it in the question above.

Apparently this was the cause of my unexpected, problem inserts in my Thing table.

But, why? Can anyone explain why this was happening? I'd like to understand it better.

Best Answer

Why are you loading the parent again when adding a new child, when you already have the object? While this is no problem for the database, it will introduce inconsistencies on the object level. You can just use the existing parent like this:

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

About the delete: have you specified cascading deletes in the database?