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

时间:2012-06-16 07:10:28

标签: c# .net entity-framework data-binding polymorphic-associations

概述/说明

简单:从添加到TEntity的{​​{1}}中添加运动类型派生的对象的多态删除不会引发ObjectSet<TEntity>上的IBindingList.ListChanged事件} IBindingList方法返回的对象。

但是,ObjectSet<TEntity>.IListSource.GetList()事件会有效地通知删除运行时类型匹配 TEntity的实例。

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

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

复制

模型设置

  1. 每种类型战略表。
  2. 在Server 2012 Express上针对已着色的SQL数据库映射和验证的实体模型。
  3. 这是实体层次结构(伪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)) && ...) { ... }
    

1 个答案:

答案 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,这就是我想要的。