实体框架4和WPF [英] Entity Framework 4 and WPF

查看:148
本文介绍了实体框架4和WPF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个WPF应用程序,使用实体框架4的MVVM设计作为ORM。我在我的视图模型中有收集属性,它将包含从EF4返回的实体集合,作为响应从业务层提交的查询的 IEnumerable< T> 集合。



我曾经希望在 ObservableCollection< T>< T>中包含 IEnumerable< T> / code>。但是,我发现自己在我的存储库中编写了更改跟踪代码,或者维护已更改对象的阴影集合,只是为了使视图模型和持久层保持同步。每次在视图模型中将一个实体添加到集合中时,我不得不去我的存储库将其添加到EF4 ObjectSet。我不得不做同样的事情更新和删除。



为了简化事情,我借了一个 EdmObservableCollection< T> WPF应用程序框架项目的$ c>类。该类包含一个 ObservableCollection< T> ,引用了一个EF4 ObjectContext ,以便OC可以更新为该集合已更新。我已经下载了 EdmObservableCollection 类。该课程运行得很好,但它有一点代码的气味,因为我最终在我的视图模型中引用了EF4。



这是我的问题:在WPF应用程序中,保持EF4实体集合与其对象上下文同步的通常方法是什么? EdmObservableCollection是一个合适的方法,还是有更好的方法?我在EF4工作中缺少一些基本的东西吗?感谢您的帮助。






  using System; 
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.Data.Objects;
使用System.Linq;

命名空间Ef4Sqlce4Demo.ViewModel.BaseClasses
{
///< summary>
/// Entity Framework 4实体集合的ObservableCollection。
///< / summary>
///< typeparam name =T>所服务的EF4实体的类型< / typeparam>
///< remarks>从WPF应用程序框架(WAF)开发http://waf.codeplex.com/</remarks>
public class EdmObservableCollection< T> :ObservableCollection< T>
{
#region Fields

//成员变量
private readonly string m_EntitySetName;
private readonly ObjectContext m_ObjectContext;

#endregion

#region构造函数

///< summary>
///创建一个新的EDM可观察集合并使用项目列表填充它。
///< / summary>
///< param name =objectContext>将管理集合的EF4 ObjectContext。< / param>
///< param name =entitySetName> EDM中实体的名称< / param>
///< param name =items>要插入到集合中的项目< / param>
public EdmObservableCollection(ObjectContext objectContext,string entitySetName,IEnumerable< T> items)
:base(items ?? new T [] {})
{
if(objectContext == null)
{
throw new ArgumentNullException(objectContext);
}
if(entitySetName == null)
{
throw new ArgumentNullException(entitySetName);
}

m_ObjectContext = objectContext;
m_EntitySetName = entitySetName;
}

///< summary>
///创建一个具有ObjectContext的空EDM Observable集合。
///< / summary>
///< param name =objectContext>将管理集合的EF4 ObjectContext。< / param>
///< param name =entitySetName> EDM中实体的名称< / param>
public EdmObservableCollection(ObjectContext objectContext,string entitySetName)
:this(objectContext,entitySetName,null)
{
}

///< summary> ;
///创建一个空的EDM Observable Collection,没有ObjectContext。
///< / summary>
///< remarks>
///在使用
/// ObjectContext来处理之前,我们使用这个构造函数来创建一个占位符集合。当程序首次启动时,这种状态发生在文件打开之前
///。我们需要在应用程序的
/// ViewModels中初始化集合,以便MainWindow可以获取零和零的Note和Tag计数。
///< / remarks>
public EdmObservableCollection()
{
}

#endregion

#region方法覆盖

protected override void InsertItem(int index,T item)
{
base.InsertItem(index,item);
m_ObjectContext.AddObject(m_EntitySetName,item);
}

protected override void RemoveItem(int index)
{
T itemToDelete = this [index];
base.RemoveItem(index);
m_ObjectContext.DeleteObject(itemToDelete);
}

protected override void ClearItems()
{
T [] itemsToDelete = this.ToArray();
base.ClearItems();

foreach(itemToDelete中的T项)
{
m_ObjectContext.DeleteObject(item);
}
}

protected override void SetItem(int index,T item)
{
T itemToReplace = this [index];
base.SetItem(index,item);

m_ObjectContext.DeleteObject(itemToReplace);
m_ObjectContext.AddObject(m_EntitySetName,item);
}

#endregion

#region公共方法

///< summary>
///将对象添加到集合的末尾。
///< / summary>
///< param name =item>要添加到集合结尾的对象。< / param>
public new void Add(T item)
{
InsertItem(Count,item);
}

///< summary>
///从集合中删除所有元素。
///< / summary>
///< param name =clearFromContext>项目是否也应该从ObjectContext中删除。< / param>
public void Clear(bool clearFromContext)
{
if(clearFromContext)
{
foreach(项目中的T项)
{
m_ObjectContext .DeleteObject(项目);
}
}

base.Clear();
}

///< summary>
///将元素插入到指定索引的集合中。
///< / summary>
///< param name =index>应该插入项目的从零开始的索引。< / param>
///< param name =item>要插入的对象。< / param>
public new void Insert(int index,T item)
{
base.Insert(index,item);
m_ObjectContext.AddObject(m_EntitySetName,item);
}

///< summary>
///更新ObjectContext以更改集合。
///< / summary>
public void Refresh()
{
m_ObjectContext.SaveChanges();
}

///< summary>
///从集合中删除特定对象的第一次出现。
///< / summary>
///< param name =item>要从集合中删除的对象。< / param>
public new void Remove(T item)
{
base.Remove(item);
m_ObjectContext.DeleteObject(item);
}

#endregion
}
}


解决方案

我想我已经做出了答案。问题不在于集合,它与传递给集合的是什么。该集合不应该直接与ObjectContext一起工作;相反,它应该与Repository一起使用它收集的实体类型。所以,Repository类应该被传递给集合的构造函数,并且应该通过对Repository方法的简单调用代替集合中的所有持久性代码。修改后的收藏类显示如下:






编辑: Slauma询问数据验证他的回应),所以我已经将CollectionChanging事件添加到我最初发布在我的答案中的集合类中。谢谢,Slauma,为了抓住!客户端代码应该订阅该事件并使用它来执行验证。将EventArgs.Cancel属性设置为true以取消更改。



集合类

 使用系统; 
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.Linq;
使用Ef4Sqlce4Demo.Persistence.Interfaces;
使用Ef4Sqlce4Demo.ViewModel.Utility;

命名空间Ef4Sqlce4Demo.ViewModel.BaseClasses
{
///< summary>
/// Entity Framework 4实体集合的ObservableCollection。
///< / summary>
///< typeparam name =T>所服务的EF4实体的类型< / typeparam>
public class FsObservableCollection< T> :ObservableCollection< T>其中T:class
{
#region Fields

//成员变量
private readonly IRepository< T> m_Repository;

#endregion

#region构造函数

///< summary>
///创建一个新的FS Observable Collection,并用一个项目列表填充它。
///< / summary>
///< param name =items>要插入到集合中的项目< / param>
///< param name =repository>类型为T的存储库。< / param>
public FsObservableCollection(IEnumerable< T> items,IRepository< T>存储库):base(items ?? new T [] {})
{
/ *上面的基类构造函数调用null-coalescing运算符(
*双问号),如果
*中传递的值为null,则指定默认值。基类构造函数调用传递一个新的类型为t,
*的空数组,它与调用没有参数的构造函数具有相同的效果 -
*创建一个新的空集合。 * /

if(repository == null)throw new ArgumentNullException(repository);
m_Repository = repository;
}

///< summary>
///创建一个带有存储库的空FS Observable集合。
///< / summary>
///< param name =repository>类型为T的存储库。< / param>
public FsObservableCollection(IRepository< T>存储库):base()
{
m_Repository = repository;
}

#endregion

#region活动

///< summary>
///在收集更改之前发生,提供取消更改的机会。
///< / summary>
public event CollectionChangingEventHandler< T> CollectionChanging;

#endregion

#region保护方法覆盖

///< summary>
///将元素插入到指定索引的集合中。
///< / summary>
///< param name =index>应该插入项目的从零开始的索引。< / param>
///< param name =item>要插入的对象。< / param>
protected override void InsertItem(int index,T item)
{
// Raise CollectionChanging事件;退出,如果更改已取消
var newItems = new List< T>(new [] {item});
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add,null,newItems);
如果(取消)返回;

//插入新项
base.InsertItem(index,item);
m_Repository.Add(item);
}

///< summary>
///删除集合的指定索引处的项目。
///< / summary>
///< param name =index>要删除的元素从零开始的索引。< / param>
protected override void RemoveItem(int index)
{
//初始化
var itemToRemove = this [index];

//提升CollectionChanging事件;如果更改被取消,退出
var oldItems = new List< T>(new [] {itemToRemove});
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove,oldItems,null);
如果(取消)返回;

//删除新项
base.RemoveItem(index);
m_Repository.Delete(itemToRemove);
}

///< summary>
///从集合中删除所有项目。
///< / summary>
protected override void ClearItems()
{
//初始化
var itemsToDelete = this.ToArray();

//提升CollectionChanging事件;如果更改已取消,退出
var oldItems = new List< T>(itemsToDelete);
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove,oldItems,null);
如果(取消)返回;

//从集合中删除所有项目。
base.ClearItems();
foreach(itemToDelete中的var item)
{
m_Repository.Delete(item);
}
}

///< summary>
///替换指定索引处的元素。
///< / summary>
///< param name =index>要替换的元素从零开始的索引。< / param>
///< param name =newItem>指定索引处的元素的新值< / param>
protected override void SetItem(int index,T newItem)
{
//初始化
var itemToReplace = this [index];

//提升CollectionChanging事件;退出如果更改已取消
var oldItems = new List< T>(new [] {itemToReplace});
var newItems = new List< T>(new [] {newItem});
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Replace,oldItems,newItems);
如果(取消)返回;

// Rereplace item
base.SetItem(index,newItem);

m_Repository.Delete(itemToReplace);
m_Repository.Add(newItem);
}

#endregion

#region公共方法覆盖

///< summary>
///将对象添加到集合的末尾。
///< / summary>
///< param name =item>要添加到集合结尾的对象。< / param>
public new void Add(T item)
{
//提升CollectionChanging事件;退出,如果更改已取消
var newItems = new List< T>(new [] {item});
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add,null,newItems);
如果(取消)返回;

//添加新项
base.Add(item);
m_Repository.Add(item);
}

///< summary>
///从集合和数据存储中删除所有元素。
///< / summary>
public new void Clear()
{
/ *我们使用'clearFromDataStore'
*参数调用此方法的重载,将其值硬编码为true。 * /

//调用重载参数
this.Clear(true);
}

///< summary>
///从集合中删除所有元素。
///< / summary>
///< param name =clearFromDataStore>项目是否也应该从数据存储中删除。< / param>
public void Clear(bool clearFromDataStore)
{
//初始化
var itemsToDelete = this.ToArray();

//提升CollectionChanging事件;如果更改已取消,退出
var oldItems = new List< T>(itemsToDelete);
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove,oldItems,null);
如果(取消)返回;

//从集合中删除所有项目。
base.Clear();

//如果不从数据存储中删除,则退出
if(!clearFromDataStore)return;

//从数据存储中删除所有项目
foreach(itemsToDelete中的var项目)
{
m_Repository.Delete(item);
}
}

///< summary>
///将元素插入到指定索引的集合中。
///< / summary>
///< param name =index>应该插入项目的从零开始的索引。< / param>
///< param name =item>要插入的对象。< / param>
public new void Insert(int index,T item)
{
//提升CollectionChanging事件;退出,如果更改已取消
var newItems = new List< T>(new [] {item});
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add,null,newItems);
如果(取消)返回;

//插入新项
base.Insert(index,item);
m_Repository.Add(item);
}

///< summary>
///将对集合的更改保留到数据存储。
///< / summary>
public void PersistToDataStore()
{
m_Repository.SaveChanges();
}

///< summary>
///从集合中删除特定对象的第一次出现。
///< / summary>
///< param name =itemToRemove>要从集合中删除的对象。< / param>
public new void Remove(T itemToRemove)
{
//提升CollectionChanging事件;如果更改被取消,退出
var oldItems = new List< T>(new [] {itemToRemove});
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove,oldItems,null);
如果(取消)返回;

//删除目标项
base.Remove(itemToRemove);
m_Repository.Delete(itemToRemove);
}

#endregion

#region私人方法

///< summary>
///引发CollectionChanging事件。
///< / summary>
///< returns>如果订阅者取消了更改,则为True。否则为false。< / returns>
private bool RaiseCollectionChangingEvent(NotifyCollectionChangingAction action,IList< T> oldItems,IList< T> newItems)
{
//如果没有订阅者退出
if(CollectionChanging == null)return假;

//创建事件args
var e = new NotifyCollectionChangingEventArgs< T>(action,oldItems,newItems);

//提升事件
this.CollectionChanging(this,e);

/ *订阅者可以在事件args上设置Cancel属性;
*事件args将反映事件发生后的更改。 * /

//设置返回值
return e.Cancel;
}

#endregion
}
}

事件精灵类

  using System; 
使用System.Collections.Generic;

命名空间Ef4Sqlce4Demo.ViewModel.Utility
{

#region枚举

///< summary>
///描述引起CollectionChanging事件的操作。
///< / summary>
public enum NotifyCollectionChangingAction {添加,删除,替换,移动,重置}

#endregion

#region代理

/// <总结>
///在添加,删除,更改,移动或整个列表被刷新之前发生。
///< / summary>
///< typeparam name =T>集合中元素的类型< / typeparam>
///< param name =sender>引发事件的对象< / param>
///< param name =e>有关事件的信息。< / param>
public delegate void CollectionChangingEventHandler< T>(object sender,NotifyCollectionChangingEventArgs< T> e);

#endregion

#region事件Args

public class NotifyCollectionChangingEventArgs< T> :EventArgs
{
#region构造函数

///< summary>
///所有参数的构造方法。
///< / summary>
///< param name =action>导致事件的操作。 < / PARAM>
///< param name =oldItems>受替换,删除或移动操作影响的项目列表< / param>
///< param name =newItems>更改中涉及的新项目列表。< / param>
///< param name =oldStartingIndex>发生移动,删除或替换操作的索引。< / param>
///< param name =newStartingIndex>发生更改的索引。< / param>
public NotifyCollectionChangingEventArgs(NotifyCollectionChangingAction action,IList< T> oldItems,IList< T> newItems,int oldStartingIndex,int newStartingIndex)
{
this.Action = action;
this.OldItems = oldItems;
this.NewItems = newItems;
this.OldStartingIndex = oldStartingIndex;
this.NewStartingIndex = newStartingIndex;
this.Cancel = false;
}

///< summary>
///省略起始索引参数的构造方法。
///< / summary>
///< param name =action>导致事件的操作。 < / PARAM>
///< param name =oldItems>受替换,删除或移动操作影响的项目列表< / param>
///< param name =newItems>更改中涉及的新项目列表。< / param>
public NotifyCollectionChangingEventArgs(NotifyCollectionChangingAction action,IList< T> oldItems,IList< T> newItems)
{
this.Action = action;
this.OldItems = oldItems;
this.NewItems = newItems;
this.OldStartingIndex = -1;
this.NewStartingIndex = -1;
this.Cancel = false;
}

#endregion

#region属性

///< summary>
///获取导致事件的操作。
///< / summary>
public NotifyCollectionChangingAction Action {get;私人集合}

///< summary>
///是否取消挂起的更改。
///< / summary>
///< remarks>此属性由事件订阅者设置。它允许
///订阅者取消挂起的更改。< / remarks>
public bool取消{get;组; }

///< summary>
///获取更改涉及的新项目列表。
///< / summary>
public IList< T> NewItems {get;私人集合}

///< summary>
///获取发生更改的索引。
///< / summary>
public int NewStartingIndex {get;组; }

///< summary>
///获取受替换,删除或移动操作影响的项目列表。
///< / summary>
public IList< T> OldItems {get;私人集合}

///< summary>
///获取移动,删除或替换操作发生的索引。
///< / summary>
public int OldStartingIndex {get;组; }

#endregion

}

#endregion
}


I am writing a WPF application, using an MVVM design with Entity Framework 4 as the ORM. I have collection properties in my view model that will contain collections of entities returned from EF4 as IEnumerable<T> collections in response to queries submitted from the business layer.

I had hoped to simply wrap the IEnumerable<T> result set in an ObservableCollection<T>. However, I found myself writing change-tracking code in my repository, or maintaining shadow collections of changed objects, just to keep the view model and persistence layer in sync. Every time an entity is added to the collection in the view model, I had to go to my repository to add it to the EF4 ObjectSet. I had to do the same sort of thing with updates and deletions.

To simplify things, I borrowed an EdmObservableCollection<T> class from the WPF Application Framework project on CodePlex (http://waf.codeplex.com/). The class wraps an ObservableCollection<T> with a reference to an EF4 ObjectContext, so that the OC can be updated as the collection is updated. I have reprinted the EdmObservableCollection class below. The class works pretty well, but it has a bit of a code smell about it, because I end up with a reference to EF4 in my view model.

Here's my question: In a WPF application, what's the usual way of keeping an EF4 entity collection in sync with its object context? Is the EdmObservableCollection a suitable approach, or is there a better way? Am I missing something fundamental in working with EF4? Thanks for your help.


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Objects;
using System.Linq;

namespace Ef4Sqlce4Demo.ViewModel.BaseClasses
{
    /// <summary>
    /// An ObservableCollection for Entity Framework 4 entity collections.
    /// </summary>
    /// <typeparam name="T">The type of EF4 entity served.</typeparam>
    /// <remarks>Developed from WPF Application Framework (WAF) http://waf.codeplex.com/</remarks>
     public class EdmObservableCollection<T> : ObservableCollection<T>
     {
          #region Fields

          // Member variables
          private readonly string m_EntitySetName;
          private readonly ObjectContext m_ObjectContext;

          #endregion

          #region Constructors

          /// <summary>
          /// Creates a new EDM Observable Collection and populates it with a list of items.
          /// </summary>
          /// <param name="objectContext">The EF4 ObjectContext that will manage the collection.</param>
          /// <param name="entitySetName">The name of the entity set in the EDM.</param>
          /// <param name="items">The items to be inserted into the collection.</param>
          public EdmObservableCollection(ObjectContext objectContext, string entitySetName, IEnumerable<T> items)
               : base(items ?? new T[] {})
          {
               if (objectContext == null)
               {
                    throw new ArgumentNullException("objectContext");
               }
               if (entitySetName == null)
               {
                    throw new ArgumentNullException("entitySetName");
               }

               m_ObjectContext = objectContext;
               m_EntitySetName = entitySetName;
          }

          /// <summary>
          /// Creates an empty EDM Observable Collection that has an ObjectContext.
          /// </summary>
          /// <param name="objectContext">The EF4 ObjectContext that will manage the collection.</param>
          /// <param name="entitySetName">The name of the entity set in the EDM.</param>
          public EdmObservableCollection(ObjectContext objectContext, string entitySetName)
               : this(objectContext, entitySetName, null)
          {
          }

          /// <summary>
          /// Creates an empty EDM Observable Collection, with no ObjectContext.
          /// </summary>
          /// <remarks>
          /// We use this constructor to create a placeholder collection before we have an
          /// ObjectContext to work with. This state occurs when the program is first launched,
          /// before a file is open. We need to initialize collections in the application's
          /// ViewModels, so that the MainWindow can get Note and Tag counts, which are zero.
          /// </remarks>
          public EdmObservableCollection()
          {
          }

          #endregion

          #region Method Overrides

          protected override void InsertItem(int index, T item)
          {
               base.InsertItem(index, item);
               m_ObjectContext.AddObject(m_EntitySetName, item);
          }

          protected override void RemoveItem(int index)
          {
               T itemToDelete = this[index];
               base.RemoveItem(index);
               m_ObjectContext.DeleteObject(itemToDelete);
          }

          protected override void ClearItems()
          {
               T[] itemsToDelete = this.ToArray();
               base.ClearItems();

               foreach (T item in itemsToDelete)
               {
                    m_ObjectContext.DeleteObject(item);
               }
          }

          protected override void SetItem(int index, T item)
          {
               T itemToReplace = this[index];
               base.SetItem(index, item);

               m_ObjectContext.DeleteObject(itemToReplace);
               m_ObjectContext.AddObject(m_EntitySetName, item);
          }

          #endregion

          #region Public Methods

          /// <summary>
          /// Adds an object to the end of the collection.
          /// </summary>
          /// <param name="item">The object to be added to the end of the collection.</param>
          public new void Add(T item)
          {
               InsertItem(Count, item);
          }

          /// <summary>
          /// Removes all elements from the collection.
          /// </summary>
          /// <param name="clearFromContext">Whether the items should also be deleted from the ObjectContext.</param>
          public void Clear(bool clearFromContext)
          {
               if (clearFromContext)
               {
                    foreach (T item in Items)
                    {
                         m_ObjectContext.DeleteObject(item);
                    }
               }

               base.Clear();
          }

          /// <summary>
          /// Inserts an element into the collection at the specified index.
          /// </summary>
          /// <param name="index">The zero-based index at which item should be inserted.</param>
          /// <param name="item">The object to insert.</param>
          public new void Insert(int index, T item)
          {
               base.Insert(index, item);
               m_ObjectContext.AddObject(m_EntitySetName, item);
          }

          /// <summary>
          /// Updates the ObjectContext for changes to the collection.
          /// </summary>
          public void Refresh()
          {
               m_ObjectContext.SaveChanges();
          }

          /// <summary>
          /// Removes the first occurrence of a specific object from the collection.
          /// </summary>
          /// <param name="item">The object to remove from the collection.</param>
          public new void Remove(T item)
          {
               base.Remove(item);
               m_ObjectContext.DeleteObject(item);
          }

          #endregion
     }
}

解决方案

I think I have worked out the answer. The problem isn't with the collection, it's with what is being passed to the collection. The collection shouldn't be working directly with the ObjectContext; instead, it should work with the Repository for the type of entity that it collects. So, a Repository class should be passed to the collection's constructor, and all the persistence code in the collection should be replaced by simple calls to Repository methods. The revised collection class appears below:


EDIT: Slauma asked about data validation (see his response), so I have added a CollectionChanging event to the collection class I originally posted in my answer. Thanks, Slauma, for the catch! Client code should subscribe to the event and use it to perform validation. Set the EventArgs.Cancel property to true to cancel a change.

The Collection Class

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Ef4Sqlce4Demo.Persistence.Interfaces;
using Ef4Sqlce4Demo.ViewModel.Utility;

namespace Ef4Sqlce4Demo.ViewModel.BaseClasses
{
    /// <summary>
    /// An ObservableCollection for Entity Framework 4 entity collections.
    /// </summary>
    /// <typeparam name="T">The type of EF4 entity served.</typeparam>
    public class FsObservableCollection<T> : ObservableCollection<T> where T:class
    {
        #region Fields

        // Member variables
        private readonly IRepository<T> m_Repository;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new FS Observable Collection and populates it with a list of items.
        /// </summary>
        /// <param name="items">The items to be inserted into the collection.</param>
        /// <param name="repository">The Repository for type T.</param>
        public FsObservableCollection(IEnumerable<T> items, IRepository<T> repository) : base(items ?? new T[] {})
        {
            /* The base class constructor call above uses the null-coalescing operator (the
             * double-question mark) which specifies a default value if the value passed in 
             * is null. The base class constructor call passes a new empty array of type t, 
             * which has the same effect as calling the constructor with no parameters--
             * a new, empty collection is created. */

            if (repository == null) throw new ArgumentNullException("repository");
            m_Repository = repository;
        }

        /// <summary>
        /// Creates an empty FS Observable Collection, with a repository.
        /// </summary>
        /// <param name="repository">The Repository for type T.</param>
        public FsObservableCollection(IRepository<T> repository) : base()
        {
            m_Repository = repository;
        }

        #endregion

        #region Events

        /// <summary>
        /// Occurs before the collection changes, providing the opportunity to cancel the change.
        /// </summary>
        public event CollectionChangingEventHandler<T> CollectionChanging;

        #endregion

        #region Protected Method Overrides

        /// <summary>
        /// Inserts an element into the Collection at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which item should be inserted.</param>
        /// <param name="item">The object to insert.</param>
        protected override void InsertItem(int index, T item)
        {
            // Raise CollectionChanging event; exit if change cancelled
            var newItems = new List<T>(new[] {item});
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add, null, newItems);
            if (cancelled) return;

            // Insert new item
            base.InsertItem(index, item);
            m_Repository.Add(item);
        }

        /// <summary>
        /// Removes the item at the specified index of the collection.
        /// </summary>
        /// <param name="index">The zero-based index of the element to remove.</param>
        protected override void RemoveItem(int index)
        {
            // Initialize
            var itemToRemove = this[index];

            // Raise CollectionChanging event; exit if change cancelled
            var oldItems = new List<T>(new[] { itemToRemove });
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
            if (cancelled) return;

            // Remove new item
            base.RemoveItem(index);
            m_Repository.Delete(itemToRemove);
        }

        /// <summary>
        /// Removes all items from the collection.
        /// </summary>
        protected override void ClearItems()
        {
            // Initialize
            var itemsToDelete = this.ToArray();

            // Raise CollectionChanging event; exit if change cancelled
            var oldItems = new List<T>(itemsToDelete);
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
            if (cancelled) return;

            // Removes all items from the collection.
            base.ClearItems();
            foreach (var item in itemsToDelete)
            {
                m_Repository.Delete(item);
            }
        }

        /// <summary>
        /// Replaces the element at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the element to replace.</param>
        /// <param name="newItem">The new value for the element at the specified index.</param>
        protected override void SetItem(int index, T newItem)
        {
            // Initialize
            var itemToReplace = this[index];

            // Raise CollectionChanging event; exit if change cancelled
            var oldItems = new List<T>(new[] { itemToReplace });
            var newItems = new List<T>(new[] { newItem });
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Replace, oldItems, newItems);
            if (cancelled) return;

            // Rereplace item
            base.SetItem(index, newItem);

            m_Repository.Delete(itemToReplace);
            m_Repository.Add(newItem);
        }

        #endregion

        #region Public Method Overrides

        /// <summary>
        /// Adds an object to the end of the collection.
        /// </summary>
        /// <param name="item">The object to be added to the end of the collection.</param>
        public new void Add(T item)
        {
            // Raise CollectionChanging event; exit if change cancelled
            var newItems = new List<T>(new[] { item });
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add, null, newItems);
            if (cancelled) return;

            // Add new item
            base.Add(item);
            m_Repository.Add(item);
        }

        /// <summary>
        /// Removes all elements from the collection and from the data store.
        /// </summary>
        public new void Clear()
        {
            /* We call the overload of this method with the 'clearFromDataStore'
             * parameter, hard-coding its value as true. */

            // Call overload with parameter
            this.Clear(true);
        }

        /// <summary>
        /// Removes all elements from the collection.
        /// </summary>
        /// <param name="clearFromDataStore">Whether the items should also be deleted from the data store.</param>
        public void Clear(bool clearFromDataStore)
        {
            // Initialize
            var itemsToDelete = this.ToArray();

            // Raise CollectionChanging event; exit if change cancelled
            var oldItems = new List<T>(itemsToDelete);
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
            if (cancelled) return;

            // Remove all items from the collection.
            base.Clear();

            // Exit if not removing from data store
            if (!clearFromDataStore) return;

            // Remove all items from the data store
            foreach (var item in itemsToDelete)
            {
                m_Repository.Delete(item);
            }
        }

        /// <summary>
        /// Inserts an element into the collection at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which item should be inserted.</param>
        /// <param name="item">The object to insert.</param>
        public new void Insert(int index, T item)
        {
            // Raise CollectionChanging event; exit if change cancelled
            var newItems = new List<T>(new[] { item });
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add, null, newItems);
            if (cancelled) return;

            // Insert new item
            base.Insert(index, item);
            m_Repository.Add(item);
        }

        /// <summary>
        /// Persists changes to the collection to the data store.
        /// </summary>
        public void PersistToDataStore()
        {
            m_Repository.SaveChanges();
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the collection.
        /// </summary>
        /// <param name="itemToRemove">The object to remove from the collection.</param>
        public new void Remove(T itemToRemove)
        {
            // Raise CollectionChanging event; exit if change cancelled
            var oldItems = new List<T>(new[] { itemToRemove });
            var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
            if (cancelled) return;

            // Remove target item
            base.Remove(itemToRemove);
            m_Repository.Delete(itemToRemove);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Raises the CollectionChanging event.
        /// </summary>
        /// <returns>True if a subscriber cancelled the change, false otherwise.</returns>
        private bool RaiseCollectionChangingEvent(NotifyCollectionChangingAction action, IList<T> oldItems, IList<T> newItems)
        {
            // Exit if no subscribers
            if (CollectionChanging == null) return false;

            // Create event args
            var e = new NotifyCollectionChangingEventArgs<T>(action, oldItems, newItems);

            // Raise event
            this.CollectionChanging(this, e);

            /* Subscribers can set the Cancel property on the event args; the 
             * event args will reflect that change after the event is raised. */

            // Set return value
            return e.Cancel;
        }

        #endregion
    }
}

The Event Args Class

using System;
using System.Collections.Generic;

namespace Ef4Sqlce4Demo.ViewModel.Utility
{

    #region Enums

    /// <summary>
    /// Describes the action that caused a CollectionChanging event. 
    /// </summary>
    public enum NotifyCollectionChangingAction { Add, Remove, Replace, Move, Reset }

    #endregion

    #region Delegates

    /// <summary>
    /// Occurs before an item is added, removed, changed, moved, or the entire list is refreshed.
    /// </summary>
    /// <typeparam name="T">The type of elements in the collection.</typeparam>
    /// <param name="sender">The object that raised the event.</param>
    /// <param name="e">Information about the event.</param>
    public delegate void CollectionChangingEventHandler<T>(object sender, NotifyCollectionChangingEventArgs<T> e);

    #endregion

    #region Event Args

   public class NotifyCollectionChangingEventArgs<T> : EventArgs
    {
        #region Constructors

        /// <summary>
        /// Constructor with all arguments.
        /// </summary>
        /// <param name="action">The action that caused the event. </param>
        /// <param name="oldItems">The list of items affected by a Replace, Remove, or Move action.</param>
        /// <param name="newItems">The list of new items involved in the change.</param>
        /// <param name="oldStartingIndex">The index at which a Move, Remove, or Replace action is occurring.</param>
        /// <param name="newStartingIndex">The index at which the change is occurring.</param>
       public NotifyCollectionChangingEventArgs(NotifyCollectionChangingAction action, IList<T> oldItems, IList<T> newItems, int oldStartingIndex, int newStartingIndex)
        {
            this.Action = action;
            this.OldItems = oldItems;
            this.NewItems = newItems;
            this.OldStartingIndex = oldStartingIndex;
            this.NewStartingIndex = newStartingIndex;
            this.Cancel = false;
        }

        /// <summary>
        /// Constructor that omits 'starting index' arguments.
        /// </summary>
        /// <param name="action">The action that caused the event. </param>
        /// <param name="oldItems">The list of items affected by a Replace, Remove, or Move action.</param>
        /// <param name="newItems">The list of new items involved in the change.</param>
       public NotifyCollectionChangingEventArgs(NotifyCollectionChangingAction action, IList<T> oldItems, IList<T> newItems)
        {
            this.Action = action;
            this.OldItems = oldItems;
            this.NewItems = newItems;
            this.OldStartingIndex = -1;
            this.NewStartingIndex = -1;
            this.Cancel = false;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the action that caused the event. 
        /// </summary>
        public NotifyCollectionChangingAction Action { get; private set; }

        /// <summary>
        /// Whether to cancel the pending change.
        /// </summary>
        /// <remarks>This property is set by an event subscriber. It enables
        /// the subscriber to cancel the pending change.</remarks>
        public bool Cancel { get; set; }

        /// <summary>
        /// Gets the list of new items involved in the change.
        /// </summary>
        public IList<T> NewItems { get; private set; }

        /// <summary>
        /// Gets the index at which the change is occurring.
        /// </summary>
        public int NewStartingIndex { get; set; }

        /// <summary>
        /// Gets the list of items affected by a Replace, Remove, or Move action.
        /// </summary>
        public IList<T> OldItems { get; private set; }

        /// <summary>
        /// Gets the index at which a Move, Remove, or Replace action is occurring.
        /// </summary>
        public int OldStartingIndex { get; set; }

        #endregion

    }

    #endregion
 }

这篇关于实体框架4和WPF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆