4

概述/描述

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

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

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

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

复制

模型设置

  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

很公平,错误报告给了微软——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,但是定义我自己的 APIObservableCollection<T>可以让我现在继续使用ObjectContext,这就是我想要的。

于 2012-07-19T15:15:06.773 回答