概述/说明
简单:从添加到TEntity
的{{1}}中添加运动类型派生的对象的多态删除不会引发ObjectSet<TEntity>
上的IBindingList.ListChanged
事件} IBindingList
方法返回的对象。
但是,ObjectSet<TEntity>.IListSource.GetList()
事件会有效地通知删除运行时类型匹配 TEntity
的实例。
为了澄清,在任何时候,对象都会从底层集合或数据视图/存储中被有效地删除,但是当这些对象是严格派生自实际ListChanged
的类型的实例时,TEntity
不提出事件通知他们被移除。
对于集合的运行时多态性的适当数据绑定支持,这只是一个惊人的BUG。
复制
模型设置
这是实体层次结构(伪UML):
ListChanged
演示代码
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)) && ...) { ... }
答案 0 :(得分:1)
公平地说,bug报告给了微软 - http://connect.microsoft.com/VisualStudio/feedback/details/749368 ......他们似乎已经承认了这个问题,但目前还不清楚他们会做什么。
请记住,为了数据绑定的目的,我们讨论IBindingList
在被ObjectSet<T>
检索时检索到的IListSource
实现,因此预计该事件会被触发,就像同类清单的情况一样。
我通过定义一个继承自ObservableCollection<T>
并包装ObjectSet<T>
的类,然后在IListSource
扩展方法的帮助下实现DbExtensions.ToBindingList<T>(this ObservableCollection<T>)
来解决这个问题。
或者,我可以继续完全开始使用DbContext
API,但定义我自己的ObservableCollection<T>
可以让我继续使用ObjectContext
,这就是我想要的。