微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 添加到ObjectSet的对象的多态删除不会引发ObjectSet.IListSource.GetList()上的IBindingList.ListChanged

概览/描述

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

但是,在ListChanged事件上有效地通知删除运行时类型与TEntity匹配的实例.

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

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

REPLICATION

型号设置

>每种类型策略表.
>在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.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()) && ...) { ... }

解决方法

很公平,bug报告给微软 – http://connect.microsoft.com/VisualStudio/feedback/details/749368 ……他们似乎已经承认了这个问题,但目前还不清楚他们会做什么.

记住,我们正在谈论由ObjectSet< T>检索的IBindingList实现.当出于数据绑定的目的被视为IListSource时,预期事件将被触发,就像它对同类列表的情况一样.

我通过定义一个继承自ObservableCollection< T>的类来解决这个问题.并且包装ObjectSet< T>,然后在DbExtensions.ToBindingList< T>(此ObservableCollection< T>)扩展方法的帮助下实现IListSource.

或者,我可以继续完全开始使用DbContext API,但是定义我自己的ObservableCollection< T>允许我现在继续使用ObjectContext,这就是我想要的.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐