4

概要/説明

シンプル:から派生した実行時型のオブジェクトのポリモーフィックな削除は、メソッドのによって返されたオブジェクトでイベントを発生TEntityObjectSet<TEntity>せません。IBindingList.ListChangedIBindingListObjectSet<TEntity>.IListSource.GetList()

ただし、ランタイム タイプが一致 するインスタンスの削除はTEntity、イベントで効果的に通知されListChangedます。

明確にするために、オブジェクトは常に基になるコレクションまたはデータ ビュー/ストアから効果的に削除されますが、これらのオブジェクトが実際にTEntity使用されたものから厳密に派生した型のインスタンスである場合、ListChangedイベントは発生せず、それらの削除を通知します。

これは、コレクションのランタイム ポリモーフィズムの適切なデータ バインディング サポートを目的とした単なる驚異的なバグです。

複製

モデル設定

  1. タイプ戦略ごとのテーブル。
  2. Server 2012 Express 上の生成された SQL データベースに対してマッピングおよび検証されたエンティティ モデル。

これはエンティティ階層 (疑似 UML) です。

FiascoEntityContext : ObjectContext
  + Foos : ObjectSet<Foo>

Foo : EntityObject
  + Id: Int32
  + Name: String

SpecialFoo : Foo
  + SpecialProperty: String

デモコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Data.Objects;

namespace FiascoEF {
    class Program {
        static void Main(string[] args) {
            using (FiascoEntityContext context = new FiascoEntityContext()) {
                //
                // add some foos
                //
                context.Foos.AddObject(new Foo { Name = "Foo1" });
                context.Foos.AddObject(new BetterFoo { Name = "BetterFoo1", SpecialProperty = "Something Special" });
                context.SaveChanges();
                //
                // show the contents
                //
                Console.WriteLine("Listing all foos:");
                foreach (var foo in context.Foos) {
                    Console.WriteLine("     Got {0}: Id={1} Name={2} (State={3})", foo, foo.Id, foo.Name, foo.EntityState);
                }
                //
                // attach handler for the ListChanged event of the IBindingList returned by context.Foos as IListSource
                // NOTE: have to do this here bacause SaveChanges() above will reset the internal IBindingList
                //
                var bindingList = (context.Foos as IListSource).GetList() as IBindingList;
                bindingList.ListChanged += new ListChangedEventHandler(bindingList_ListChanged);
                //
                // delete all foos and show state. expect the event handler above to be invoked.
                //
                Console.WriteLine("Deleting all foos:");
                foreach (var foo in context.Foos) {
                    context.Foos.DeleteObject(foo);
                    Console.WriteLine("     Deleted {0}: Id={1} Name={2} (State={3})", foo, foo.Id, foo.Name, foo.EntityState);
                }
                context.SaveChanges();
            }
        }

        static void bindingList_ListChanged(object sender, ListChangedEventArgs e) {
            Console.WriteLine("     Event on {0}: {1}", sender, e.ListChangedType);
        }
    }
}

予想された結果

Listing all foos:
     Got FiascoEF.Foo: Id=257 Name=Foo1 (State=Unchanged)
     Got FiascoEF.BetterFoo: Id=258 Name=BetterFoo1 (State=Unchanged)
Deleting all foos:
     Event on System.Data.Objects.ObjectView`1[FiascoEF.Foo]: ItemDeleted
     Deleted FiascoEF.Foo: Id=257 Name=Foo1 (State=Deleted)
     Event on System.Data.Objects.ObjectView`1[FiascoEF.Foo]: ItemDeleted
     Deleted FiascoEF.BetterFoo: Id=258 Name=BetterFoo1 (State=Deleted)

実績

Listing all foos:
     Got FiascoEF.Foo: Id=257 Name=Foo1 (State=Unchanged)
     Got FiascoEF.BetterFoo: Id=258 Name=BetterFoo1 (State=Unchanged)
Deleting all foos:
     Event on System.Data.Objects.ObjectView`1[FiascoEF.Foo]: ItemDeleted
     Deleted FiascoEF.Foo: Id=257 Name=Foo1 (State=Deleted)
     Deleted FiascoEF.BetterFoo: Id=258 Name=BetterFoo1 (State=Deleted)

リサーチ

リフレクターを介して、実際IBindingListに返されたのはタイプObjectView<TElement>であることがわかりました。このタイプは、削除操作を内部の に委譲しIObjectViewData<TElement>ます。そのインターフェースで見つかった実装は、以下ObjectViewQueryResultData<TElement>を定義するものです。

public ListChangedEventArgs OnCollectionChanged(object sender, CollectionChangeEventArgs e, ObjectViewListener listener) {

    ListChangedEventArgs changeArgs = null;

    if (e.Element.GetType().IsAssignableFrom(typeof(TElement)) && _bindingList.Contains((TElement) (e.Element))) {
        ...
        changeArgs = new ListChangedEventArgs(ListChangedType.ItemDeleted, ...);
        ...
    }

    return changeArgs;
}

チェック:

if (e.Element.GetType().IsAssignableFrom(typeof(TElement)) && ...) { ... }

偽物のようです...おそらく次のことが意図されていましたか?

if (typeof(TElement).IsAssignableFrom(e.Element.GetType()) && ...) { ... }
4

1 に答える 1

1

Microsoft にバグが報告されまし

データ バインディングの目的でとして表示されたときにIBindingListによって取得される実装について話していることを思い出してください。そのため、同種リストの場合と同様に、イベントが発生することが期待されます。ObjectSet<T>IListSource

を継承しObservableCollection<T>てラップするクラスを定義し、拡張メソッドを使用してObjectSet<T>実装することで解き放ちました。IListSourceDbExtensions.ToBindingList<T>(this ObservableCollection<T>)

DbContextまたは、先に進んでAPI を完全に使い始めることもできますが、独自の API を定義することで、今のところObservableCollection<T>使い続けることができますObjectContext。これが私が望んでいることです。

于 2012-07-19T15:15:06.773 に答える