0

ADO.NET Entity Framework を理解するための助けが必要です。

ADO.NET Entity Framework を使用して、WPF TreeView コントロールで階層データを表現および操作しようとしています。

親と子を持つ ADO.NET Entity Framework オブジェクト http://img14.imageshack.us/img14/7158/thingpi1.gif

これらの各 Thing には、1 つの親と 0 個以上の子があります。

私の「削除」ボタン...

Private Sub ButtonDeleteThing_Click(...)
    db.DeleteObject(DirectCast(TreeViewThings.SelectedItem, Thing))
    デシベル.SaveChanges()
サブ終了

アプリケーションのデバッグ中に SQL Server プロファイラーを監視しています。

  1. 最初のボタンをクリックすると、問題なく削除されます。
  2. 2 回目のボタン クリックは、重複する親を挿入し (ただし、GUID の uniqueidentifier プライマリ キーは空)、削除を実行します。
  3. 空の GUID 主キーを持つ別の行を挿入できないため、3 回目のボタン クリックは失敗します (PRIMARY KEY 制約の違反)。

予期しない、生成された T-SQL の重複...

exec sp_executesql N'insert [dbo].[Thing]([Id], [ParentId], ...)
値 (@0, @1, ...) ',N'@0 一意識別子,@1 一意識別子,...',
@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" ... />

ビジュアルベーシック...

部分公開クラス Window1

    Dim db As New ThingProjectEntities

    Private Sub Window1_Loaded(...) ハンドル MyBase.Loaded
        TreeViewThings.ItemsSource = _
            t から db.Thing.Include("Children") _
            Where (t.Parent Is Nothing) _
            を選択
    サブ終了

    プライベート サブ TreeViewThings_Expanded(...)
        薄暗い ExpandedTreeViewItem を TreeViewItem として = _
            DirectCast(e.OriginalSource, TreeViewItem)
        LoadTreeViewChildren(ExpandedTreeViewItem)
    サブ終了

    Sub LoadTreeViewChildren(ByRef Parent As TreeViewItem)
        Guid = DirectCast(Parent.DataContext, Thing).Id として Dim ParentId
        System.Linq.IQueryable(Of Thing) としての薄暗い ChildThings
        ChildThings = From t In db.Thing.Include("Children") _
                      どこで t.Parent.Id = ParentId _
                      を選択
        Parent.ItemsSource = ChildThings
    サブ終了

    Private Sub ButtonAddThing_Click(...)
        NewThing を新しいものとして薄暗くする
        NewThing.Id = Guid.NewGuid()
        親 ID を Guid として暗くする = _
            DirectCast(TreeViewThings.SelectedItem, Thing).Id
        NewThing.Parent = (db.Thing の t から _
                           どこで t.Id = ParentId _
                           t).First を選択します。
        ...
        デシベル。AddToThing(NewThing)
        デシベル.SaveChanges()
        TreeViewThings.UpdateLayout()
    サブ終了

    Private Sub ButtonDeleteThing_Click(...)
        db.DeleteObject(DirectCast(TreeViewThings.SelectedItem, Thing))
        デシベル.SaveChanges()
    サブ終了

    ...

クラス終了

私は何を間違っていますか?なぜこれらの奇妙な挿入が生成されるのですか?


アップデート:

私は突破口を開きました。でも、いまだに説明できません。

この質問のコードを単純化したときに、原因を取り除きました。

次のようなLinqを使用するのではなく:

From t In db.Thing.Include("Children") どこで...

私は次のようなLinqを使用しています:

From t In db.Thing.Include("Children").Include("Brand") どこで...

ご覧のとおり、My Thing エンティティは別の Brand エンティティに関連付けられています。

親と子を持つ ADO.NET Entity Framework オブジェクトと関連オブジェクト http://img25.imageshack.us/img25/3268/thingbrandct4.gif

関係ないと思ったので、上の質問には入れませんでした。

どうやらこれが、Thing テーブルへの予期しない問題の挿入の原因でした。

しかし、なぜ?なぜこれが起こったのか誰か説明できますか?私はそれをよりよく理解したいと思います。

4

3 に答える 3

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

削除について: データベースでカスケード削除を指定しましたか?

于 2009-02-24T11:17:54.997 に答える
1

コードの詳細については説明していませんが、最初に考慮すべきことは、階層のすべてのレベルでDeleteObjectを呼び出す必要はないということです。他のO/RMと同様に、EFはオブジェクトとその関連付けを追跡します。

たとえば、親->子1..*の関係があるとします。親にクエリを実行し、親のChildrenコレクションから子オブジェクトを削除してからSaveChanges()を呼び出すと、EFは適切なDELETESQLステートメントを生成します。これを自分で追跡する必要はありません。

したがって、シナリオを達成するためのより良い方法は、これを行うことです。

  1. EFからオブジェクト階層を照会します。
  2. それをUIにバインドします。必要に応じて、UIでメモリ内オブジェクトを変更します。
  3. 完了したら、SaveChangesを呼び出して、EFに何をすべきかを理解させます。

それが役に立ったら教えてください。

于 2009-02-19T20:54:15.603 に答える
1

ここでは 2 つのことが行われています。両者の関係は完全には理解できませんが、あなたを道に連れて行くことができると思います。

最初に理解しておく必要があるのは、Entity Framework は、完全に実体化されていないインスタンスの削除をうまく処理できないということです。そのため、Include によって表示される動作が変更されます。したがって、子のリストを集約するエンティティがある場合は、delete を呼び出す前にそれらの子を読み込む必要があります。子インスタンスがメモリ内にある場合のみ、親の前に削除されます。そのため、Include の有無にかかわらず、Delete を呼び出す前に次のようなことを行う必要があります。

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

リレーションシップで Include を呼び出した場合、呼び出していない場合は何もしません。

固有の理解の 2 番目のことは、既存のエンティティとの関係を持つ新しいエンティティを挿入することは、概念的には 2 つの異なる挿入であるということです。これは、リレーションシップが Entity Framework の最優先事項であるという事実の結果です。最初の挿入はエンティティ自体で、2 番目は関係です。この場合、リレーションシップ用の別個のテーブルがないため、データベースへの実際の挿入は必要に応じて 1 つだけです。ただし、Entity Framework は、正しくマップされている場合にのみこれを把握できます。

この場合、何が起こっているのでしょうか?以下は、ここで起こっていることのいくつかに基づいた私の推測です。しかし、状況は私が説明しているよりもさらに複雑だと思うので、以下の内容はいくつかの詳細で間違っていると思います. ただし、実際の問題を解決するのに十分に近い場合があります。

  1. 削除しようとする前に、完全には実体化されていないインスタンスがありました。
  2. 他の何かを削除しようとすると、フレームワークは同じ関係を調べようとしました。異常な状態を発見し、正常な状態に戻そうとしましたが、うまくいきませんでした。
  3. 次に、もう一度削除しようとしましたが、2 回繰り返しましたが、今回はデータベースの制約により、さらに成功しませんでした。
  4. インクルードを使用すると、1 の問題が修正されます。
于 2009-02-23T15:26:04.313 に答える