ADO.NET 实体框架生成意外的问题插入

发布于 07-13 22:03 字数 4031 浏览 12 评论 0原文

我需要一些帮助来理解 ADO.NET 实体框架。

我正在尝试使用 ADO.NET 实体框架在 WPF TreeView 控件中表示和操作分层数据。

具有父级和子级的 ADO.NET 实体框架对象 http://img14.imageshack.us/ img14/7158/thingpi1.gif

这些事物中的每一个都有一个单亲和零个或多个孩子。

我的“删除”按钮...

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

我在调试应用程序时监视 SQL Server Profiler:

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

您看,My Thing 实体与另一个 Brand 实体相关。

具有父级和子级的 ADO.NET 实体框架对象以及相关对象 http://img25。 imageshack.us/img25/3268/thingbrandct4.gif

我认为这是无关紧要的,所以我没有将其包含在上面的问题中。

显然,这是我在“事物”表中插入意外问题的原因。

但为什么? 谁能解释为什么会发生这种情况? 我想更好地理解它。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酷到爆炸2024-07-20 22:03:16

当您已经拥有该对象时,为什么在添加新子项时再次加载父项? 虽然这对于数据库来说不是问题,但它会导致对象级别的不一致。 您可以像这样使用现有的父级:

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

关于删除:您是否在数据库中指定了级联删除?

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?

謌踐踏愛綪2024-07-20 22:03:16

我还没有详细查看代码,但首先要考虑的是,您不需要在层次结构的每个级别调用 DeleteObject。 与其他 O/RM 一样,EF 会为您跟踪对象及其关联。

例如,假设您有父->子 1..* 关系。 如果您查询父级,从父级 Children 集合中删除子对象,然后调用 SaveChanges(),EF 将为您生成适当的 DELETE SQL 语句 - 您不需要自己跟踪这一点。

因此,实现您的场景的更好方法是执行以下操作:

  1. 从 EF 中查询对象层次结构。
  2. 将其绑定到您的 UI。 让您的 UI 根据您的需要修改内存中的对象。
  3. 完成后,调用 SaveChanges 并让 EF 确定要做什么。

让我知道这是否有帮助。

I haven't had a detailed look at the code but the first thing to consider is that you shouldn't need to call DeleteObject at every level of your hierarchy. EF like other O/RMs tracks objects and their associations for you.

Let's say, for example, that you have a parent->child 1..* relationship. If you query the parent, remove a child object from the parents Children collection and then call SaveChanges(), EF will generate the appropriate DELETE SQL statements for you - You do not need to track this yourself.

So, a better way to achieve your scenario would be to do this:

  1. Query the object hierarchy out of EF.
  2. Bind that to your UI. Let your UI modify the in-memory objects as you see fit.
  3. When you are done, call SaveChanges and let the EF figure out what to do.

Let me know if that helps.

-黛色若梦2024-07-20 22:03:16

这里发生了两件事。 我不太明白他们之间的关系,但我想我可以让你上路。

您需要了解的第一件事是实体框架不能很好地处理删除未完全具体化的实例。 这就是为什么 Include 会改变您所看到的行为。 因此,如果您有一个聚合子级列表的实体,则需要在调用删除之前加载这些子级。 只有当子实例在内存中时,它们才会在父实例之前被删除。 因此,无论是否包含 Include,您都需要在调用 Delete 之前执行类似的操作。

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

如果您已经在关系上调用了 Include,那么这将不会执行任何操作(如果您没有这样做),那么它将确保一切都在您之前实现。

唯一需要理解的第二件事是 插入一个新实体与现有实体的关系在概念上是两个不同的插入。 这是因为关系在实体框架中是一流的。 第一个插入是实体本身,第二个插入是关系。 在这种情况下,没有单独的关系表,因此只有根据需要向数据库进行一次实际插入。 然而,实体框架只有在正确映射的情况下才能解决这个问题。

那么这个案例到底是怎么回事呢? 以下是我根据我所看到的一些事情进行的推测。 但我认为情况比我描述的还要复杂,所以我认为下面的一些细节是不正确的。 不过,它可能足以帮助您解决实际问题。

  1. 在您尝试删除一个实例之前,该实例尚未完全实现。
  2. 当您尝试删除其他内容时,框架会尝试查看相同的关系。 它发现事情不正常,并试图让事情恢复到良好状态,但没有成功。
  3. 然后你又尝试删除,重复了2次,只是这次由于数据库限制,更不成功。
  4. 使用 Include 修复了 1 中的问题。

There are two things going on here. I don't completely understand the relationship between them, but I think I can get you on your way.

The first thing that you need to understand is that the Entity Framework does not deal well with deleting a less-than-fully-materialized instance. That is why Include changes the behavior that you see. So if you have an entity that aggregates a list of children, you need to load those children before calling delete. Only if the child instances are in memory will they be deleted before the parent. So, with or without Include, you need to do something like this before calling Delete.

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

If you've called Include on the relationship, then this will do nothing if you haven't, then it will ensure that everything is materialized before you

The second thing that unique understand is that inserting a new entity with a relationship to an existing entity is conceptually two different inserts. This is a consequence of the fact that relationships are first-class in the Entity Framework. The first insert is the entity itself, the second is the relationship. In this case, there is no separate table for the relationship, so only one actual insert to the database as needed. However, the Entity Framework can only figure this out if it is mapped correctly.

So what is going on in this case? What follows is my speculation based on some of the things I see going on here. But I think the situation is even more complicated than I'm describing, so I believe what follows is incorrect in some of the details. It may be close enough to help you solve the actual problem, though.

  1. You had an instance that was less-than-fully-materialized before you tried to delete it.
  2. When you tried to delete something else, the framework tried to look into the same relationship. It found things out of whack and tried, unsuccessfully, to put things back into a good state.
  3. Then you tried to delete again, 2 repeats, only this time it is even less successful, due to the database constraint.
  4. Using Include fixes the issue in 1.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文