我正在编写一个WPF应用程序,使用实体框架4作为ORM的MVVM设计。我的视图模型中有集合属性,它将包含从EF4返回的实体集合,作为IEnumerable<T>
集合,以响应业务层提交的查询。实体框架4和WPF
我本来希望简单地将IEnumerable<T>
结果集换成ObservableCollection<T>
。但是,我发现自己在我的存储库中编写变更跟踪代码,或者维护已更改对象的影集,只是为了保持视图模型和持久层同步。每次将实体添加到视图模型中的集合中时,我都必须前往我的存储库以将其添加到EF4 ObjectSet中。我必须做更新和删除相同的事情。
为了简化工作,我从CodePlex(http://waf.codeplex.com/)上的WPF应用程序框架项目借了一个EdmObservableCollection<T>
类。该课程包含一个参考EF4 ObjectContext
的ObservableCollection<T>
,以便可以在更新集合时更新OC。我已经重印了下面的EdmObservableCollection
课程。该类运行得非常好,但它有一些代码味道,因为我最终在我的视图模型中引用了EF4。
这里是我的问题:在WPF应用程序中,保持EF4实体集合与其对象上下文同步的常用方法是什么? EdmObservableCollection是一个合适的方法,还是有更好的方法?我在使用EF4时缺少一些基本的东西吗?谢谢你的帮助。
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
}
}
有趣和周到的点;从我+1。我看到一些不同的问题。具体来说,我做了大量的数据和业务规则验证。我使用视图模型中的CollectionChanging事件来调用服务方法来进行验证。如果验证失败,服务方法将取消操作并回调到视图以显示相应的错误消息。 – 2011-03-25 23:22:02
@David Veeneman:我不知道这个事件,有趣!它在WPF中可用吗?我只是在搜索,只能在Windows窗体命名空间中找到它。 – Slauma 2011-03-25 23:35:56
糟糕 - 这是我在另一个版本的EdmObservableCollection上实现的事件。很容易做 - 声明事件参数以通过操作(添加,更新,删除),受影响的对象以及取消标志(读/写)。在调用基类之前,在每个方法中引发事件并影响集合,如果Cancel设置为true,则退出。我将就此主题编写一篇CodeProject文章,并将在该代码中包含该事件。谢谢你的收获! – 2011-03-26 18:34:18