Dav*_*man 10 wpf entity-framework mvvm entity-framework-4
我正在编写一个WPF应用程序,使用带有Entity Framework 4的MVVM设计作为ORM.我的视图模型中有集合属性,它将包含从EF4返回的实体集合作为IEnumerable<T>集合,以响应从业务层提交的查询.
我原本希望简单地将IEnumerable<T>结果集包装成一个ObservableCollection<T>.但是,我发现自己在我的存储库中编写了更改跟踪代码,或维护了已更改对象的阴影集合,只是为了使视图模型和持久层保持同步.每次在视图模型中将实体添加到集合时,我都必须转到我的存储库以将其添加到EF4 ObjectSet.我不得不做同样的更新和删除.
为简化起见,我EdmObservableCollection<T>从CodePlex上的WPF应用程序框架项目(http://waf.codeplex.com/)借用了一个类.该类ObservableCollection<T>使用对EF4的引用进行包装ObjectContext,以便在更新集合时更新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
}
}
Run Code Online (Sandbox Code Playgroud)
我想我已经找到了答案.问题不在于集合,而是与传递给集合的内容有关.该集合不应直接与ObjectContext一起工作; 相反,它应该与Repository一起使用它所收集的实体类型.因此,应该将Repository类传递给集合的构造函数,并且应该通过对Repository方法的简单调用来替换集合中的所有持久性代码.修订后的集合类如下所示:
编辑: Slauma询问数据验证(参见他的回复),所以我已经将CollectionChanging事件添加到我最初在我的答案中发布的集合类中.谢谢,Slauma,抓住了!客户端代码应订阅该事件并使用它来执行验证.将EventArgs.Cancel属性设置为true以取消更改.
收集班
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
}
}
Run Code Online (Sandbox Code Playgroud)
事件Args类
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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6212 次 |
| 最近记录: |