ОБЗОР/ОПИСАНИЕ
Просто: полиморфное удаление объектов типов среды выполнения, производных от TEntity
, добавленных к ObjectSet<TEntity>
, НЕ вызовет событие IBindingList.ListChanged
для объекта IBindingList
, возвращаемого методом ObjectSet<TEntity>.IListSource.GetList()
.
Однако удаление экземпляров, тип среды выполнения которых соответствует TEntity
, эффективно уведомляется о событии ListChanged
.
Чтобы уточнить, в любое время объекты эффективно удаляются из базовых коллекций или представлений/хранилищ данных, но когда эти объекты являются экземплярами типов, строго производных от фактического используемого TEntity
, событие ListChanged
не возникает, чтобы уведомить об их удалении.
Это просто феноменальная ОШИБКА для целей соответствующей поддержки привязки данных полиморфизма времени выполнения для коллекций.
РЕПЛИКАЦИЯ
Настройка модели
- Таблица для стратегии типа.
- Модель объекта сопоставляется и проверяется на основе генерированной базы данных SQL на сервере Server 2012 Express.
Это иерархия сущностей (псевдо-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()) && ...) { ... }