Я думаю, что я разработал ответ.Проблема не в коллекции, а в том, что передается в коллекцию.Коллекция не должна работать напрямую с ObjectContext;вместо этого он должен работать с репозиторием для типа сущности, которую он собирает.Таким образом, класс Repository должен быть передан конструктору коллекции, и весь постоянный код в коллекции должен быть заменен простыми вызовами методов Repository.Пересмотренный класс коллекции представлен ниже:
РЕДАКТИРОВАТЬ: Слаума спросил о проверке данных (см. Его ответ), поэтому я добавил событие CollectionChanging в класс коллекции, который я первоначально разместил в своем ответе.Спасибо, Слаума, за улов!Код клиента должен подписаться на событие и использовать его для проверки.Установите для свойства 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
}
}
Класс аргументов события
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
}