添加到ObjectSet 的对象的多态删除不会引发ObjectSet上的IBindingList.ListChanged .IListSource.GetList()
概览/描述
简单:从添加到ObjectSet
TEntity
派生的运行时类型的对象的多态删除不会引发由ObjectSet.IListSource.GetList()
方法返回的IBindingList
对象上的IBindingList
事件。
但是,在ListChanged
事件上有效地通知删除运行时类型与 TEntity
匹配的实例。
为了澄清,在任何时候,对象都被有效地从底层集合或数据视图/存储中移除,但是当这些对象是严格从实际使用的TEntity
派生的类型的实例时,不会引发ListChanged
事件以通知它们的移除。
对于集合的运行时多态性的适当数据绑定支持,这只是一个惊人的BUG。
REPLICATION
型号设置
- 每种类型战略表。
- 在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器发现返回的实际IBindingList
是ObjectView
类型,并且此类型将删除操作委托给内部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
扩展方法的帮助下实现IListSource
解决DbExtensions.ToBindingList
。
或者,我可以继续完全开始使用DbContext
API,但是定义我自己的ObservableCollection
允许我现在继续使用ObjectContext
,这就是我想要的。