【问题标题】:Why does Linq.Enumerable.Where break my ObservableCollection为什么 Linq.Enumerable.Where 会破坏我的 ObservableCollection
【发布时间】:2013-06-25 05:15:57
【问题描述】:

背景:

我正在编写一个 WPF 应用程序,严格遵循 MVVM 模式。我有一个 BaseRepository 类作为连接到不同数据库的通用接口(EF 不是一个选项),一切正常;这只是一个技术问题。

我使用一个名为 NotifyingCollection 的包装 ObservableCollection 来订阅 IEditableObject 的 ItemEndEdit 事件(我的 ViewModelBase 实体包装器实现了 INotifyPropertyChanged 和 IEditableObject 成员)。

当在我的 WPF DataGrid 中编辑项目时调用 ReadAll 方法时,提供的代码示例会引发“'EditItem' is not allowed for this view”异常。 但是,如果我将方法中的行替换为注释掉的部分,则效果很好!

问题:

换句话说,它看起来像中继 Linq.Enumerable.Where 扩展方法而不是返回集合的 IEnumerable 版本,从自定义集合中删除了功能;如果它们都是 IEnumerable,为什么会发生这种情况?

代码示例:

namespace MyCompany.Common.Abstracts
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Data.Common;
    using System.Diagnostics;
    using System.Linq;
    using System.Linq.Expressions;
    using MyCompany.Common.Extensions;
    using MyCompany.Common.Utilities;


    public abstract class BaseRepository<TEntity> : IDisposable where TEntity : ViewModelBase
    {
        protected BaseRepository()
        {
            this.EntitySet = new NotifyingCollection<TEntity>();
            this.EntitySet.ItemEndEdit += new ViewModelBase.ItemEndEditEventHandler(ItemEndEdit);
            this.EntitySet.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChanged);
        }

        protected abstract NotifyingCollection<TEntity> EntitySet { get; set; }

        protected virtual void PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            Debug.WriteLine(String.Format("Modify '{0}'", e.PropertyName), "PropertyChanged");
        }

        protected virtual void ItemEndEdit(IEditableObject sender)
        {
            this.Update(sender as TEntity);
        }

        protected virtual void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            var collection = (e.Action == NotifyCollectionChangedAction.Remove) ?
                e.OldItems : e.NewItems;

            foreach (TEntity entity in collection)
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        entity.PropertyChanged += this.PropertyChanged;
                        this.Create(entity);
                        break;

                    case NotifyCollectionChangedAction.Remove:
                        entity.PropertyChanged -= this.PropertyChanged;
                        this.Delete(entity);
                        break;

                    default:
                        Debug.WriteLine(String.Format("{0} '{1}'", e.Action.ToString(), entity.DisplayName), "CollectionChanged");
                        break;
                }
            }
        }

        public virtual bool Create(TEntity entity)
        {
            Debug.WriteLine(String.Format("Create '{0}'", entity.DisplayName), "CollectionChanged");
            return true;
        }

        public virtual IEnumerable<TEntity> Read(Expression<Func<TEntity, bool>> filter)
        {
            return this.EntitySet.Where(filter.Compile());
        }

        public virtual IEnumerable<TEntity> ReadAll()
        {
            return this.Read(x => true);
            //return this.EntitySet;
        }

        public virtual bool Update(TEntity entity)
        {
            Debug.WriteLine(String.Format("Update '{0}'", entity.DisplayName), "ItemEndEdit");
            return true;
        }

        public virtual bool Delete(TEntity entity)
        {
            Debug.WriteLine(String.Format("Delete '{0}'", entity.DisplayName), "CollectionChanged");
            return true;
        }

        public virtual IEnumerable<TColumn> GetColumn<TColumn>(string columnName)
        {
            var lookupTable = this.Read(x => x != null);

            List<TColumn> column = new List<TColumn>();
            foreach (TEntity record in lookupTable)
            {
                column.Add(record.GetPropValue<TColumn>(columnName));
            }
            var result = column.Distinct();

            foreach (TColumn element in result)
            {
                yield return element;
            }
        }

        public abstract int Commit();

        public abstract DbTransaction BeginTransaction();

        public abstract void Dispose();
    }
}

【问题讨论】:

    标签: wpf linq ienumerable observablecollection ieditableobject


    【解决方案1】:

    来自 MichaelLPerry 的博客:Common mistakes while using ObservableCollection

    ObservableCollection 是必须的。 Linq 是声明性的。他们俩 如果没有额外的帮助,不能一起使用。

    命令式代码显式地作用于某事。当使用一个 ObservableCollection,您显式调用 Add、Remove 和其他 更改集合的方法。您必须准确决定何时和 如何采取这些行动。

    声明性代码隐式生成一些东西。使用 linq 时,您 声明集合的外观,如何过滤, 映射和转换。您没有显式修改集合。

    这两种范式不能混用。一旦你在一个 ObservableCollection,它不再是可观察的。有 像 Bindable Linq 这样的开源项目弥合了差距。但 如果没有这种额外的帮助,人们往往会在事情没有发生时感到惊讶 工作。

    【讨论】:

    • 找不到更好的解释,谢谢!事实上,我怀疑它正在创建一个新集合,因此不再指向相同的引用。
    【解决方案2】:

    EntitySet.Where 方法的 reurn 值将不再是原来的NotifyingCollection&lt;TEntity&gt;

    【讨论】:

      猜你喜欢
      • 2013-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-21
      • 1970-01-01
      • 2011-03-25
      相关资源
      最近更新 更多