添加到ObjectSet 的对象的多态删除不会引发ObjectSet上的IBindingList.ListChanged .IListSource.GetList()

概览/描述

简单:从添加到ObjectSet TEntity 派生的运行时类型的对象的多态删除不会引发由ObjectSet.IListSource.GetList()方法返回的IBindingList对象上的IBindingList事件。

但是,在ListChanged事件上有效地通知删除运行时类型 TEntity 匹配的实例。

为了澄清,在任何时候,对象都被有效地从底层集合或数据视图/存储中移除,但是当这些对象是严格从实际使用的TEntity派生的类型的实例时,不会引发ListChanged事件以通知它们的移除。

对于集合的运行时多态性的适当数据绑定支持,这只是一个惊人的BUG。

REPLICATION

型号设置

  1. 每种类型战略表。
  2. 在Server 2012 Express上映射和validation实体模型与已对齐的SQL数据库。

这是实体层次结构(伪UML):

 FiascoEntityContext : ObjectContext + Foos : ObjectSet 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) 

研究

通过reflection器发现返回的实际IBindingListObjectView类型,并且此类型将删除操作委托给内部IObjectViewData 。 为该接口找到的实现是ObjectViewQueryResultData ,它定义了:

 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()) && ...) { ... } 

很公平,bug报告给微软 – http://connect.microsoft.com/VisualStudio/feedback/details/749368 ……他们似乎已经承认了这个问题,但目前还不清楚他们会做什么。

记住,我们正在讨论由ObjectSet检索的IBindingList实现,当被视为IListSource用于数据绑定时,因此事件应该被触发,就像它对同类列表的情况一样。 。

我通过定义一个inheritance自ObservableCollection并包装ObjectSet ,然后在DbExtensions.ToBindingList(this ObservableCollection)扩展方法的帮助下实现IListSource解决DbExtensions.ToBindingList(this ObservableCollection)

或者,我可以继续完全开始使用DbContext API,但是定义我自己的ObservableCollection允许我现在继续使用ObjectContext ,这就是我想要的。