我发现无论做什么,EF 都要求ICollection<T>
公开。我认为这是因为当从数据库加载对象时,映射会查找集合属性,获取集合,然后调用集合的Add
方法来添加每个子对象。
我想确保添加是通过父对象上的方法完成的,因此创建了一个包装集合、捕获添加并将其定向到我首选的添加方法的解决方案。
无法扩展 aList
和其他集合类型,因为该Add
方法不是虚拟的。一种选择是扩展Collection
类并覆盖该InsertItem
方法。
我只关注界面的Add
、Remove
和Clear
功能,ICollection<T>
因为它们是可以修改集合的功能。
首先,是我的实现接口的基本集合包装器ICollection<T>
默认行为是普通集合的行为。但是,调用者可以指定要调用的替代Add
方法。此外,调用者可以通过将替代设置为 来强制Add
, Remove
,Clear
操作是不允许的null
。NotSupportedException
如果有人尝试使用该方法,这将导致被抛出。
抛出异常不如一开始就阻止访问。但是,应该对代码进行测试(单元测试),并且会很快发现异常并进行适当的代码更改。
public abstract class WrappedCollectionBase<T> : ICollection<T>
{
private ICollection<T> InnerCollection { get { return GetWrappedCollection(); } }
private Action<T> addItemFunction;
private Func<T, bool> removeItemFunction;
private Action clearFunction;
/// <summary>
/// Default behaviour is to be like a normal collection
/// </summary>
public WrappedCollectionBase()
{
this.addItemFunction = this.AddToInnerCollection;
this.removeItemFunction = this.RemoveFromInnerCollection;
this.clearFunction = this.ClearInnerCollection;
}
public WrappedCollectionBase(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) : this()
{
this.addItemFunction = addItemFunction;
this.removeItemFunction = removeItemFunction;
this.clearFunction = clearFunction;
}
protected abstract ICollection<T> GetWrappedCollection();
public void Add(T item)
{
if (this.addItemFunction != null)
{
this.addItemFunction(item);
}
else
{
throw new NotSupportedException("Direct addition to this collection is not permitted");
}
}
public void AddToInnerCollection(T item)
{
this.InnerCollection.Add(item);
}
public bool Remove(T item)
{
if (removeItemFunction != null)
{
return removeItemFunction(item);
}
else
{
throw new NotSupportedException("Direct removal from this collection is not permitted");
}
}
public bool RemoveFromInnerCollection(T item)
{
return this.InnerCollection.Remove(item);
}
public void Clear()
{
if (this.clearFunction != null)
{
this.clearFunction();
}
else
{
throw new NotSupportedException("Clearing of this collection is not permitted");
}
}
public void ClearInnerCollection()
{
this.InnerCollection.Clear();
}
public bool Contains(T item)
{
return InnerCollection.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
InnerCollection.CopyTo(array, arrayIndex);
}
public int Count
{
get { return InnerCollection.Count; }
}
public bool IsReadOnly
{
get { return ((ICollection<T>)this.InnerCollection).IsReadOnly; }
}
public IEnumerator<T> GetEnumerator()
{
return InnerCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return InnerCollection.GetEnumerator();
}
}
鉴于该基类,我们可以通过两种方式使用它。示例是使用原始帖子对象。
1)创建特定类型的包装集合(例如List
) public class WrappedListCollection : WrappedCollectionBase, IList { private List innerList;
public WrappedListCollection(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.innerList = new List<T>();
}
protected override ICollection<T> GetWrappedCollection()
{
return this.innerList;
}
<...snip....> // fill in implementation of IList if important or don't implement IList
}
然后可以使用它:
public Customer Customer
{
public ICollection<Order> Orders {get { return _orders; } }
// Public methods.
public void AddOrder(Order order)
{
_orders.AddToInnerCollection(order);
}
// Private fields.
private WrappedListCollection<Order> _orders = new WrappedListCollection<Order>(this.AddOrder, null, null);
}
2)给一个集合使用
public class WrappedCollection<T> : WrappedCollectionBase<T>
{
private ICollection<T> wrappedCollection;
public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.wrappedCollection = collectionToWrap;
}
protected override ICollection<T> GetWrappedCollection()
{
return this.wrappedCollection;
}
}
可以按如下方式使用:
{ public ICollection Orders {get { return _wrappedOrders; } } // 公共方法。
public void AddOrder(Order order)
{
_orders.Add(order);
}
// Private fields.
private ICollection<Order> _orders = new List<Order>();
private WrappedCollection<Order> _wrappedOrders = new WrappedCollection<Order>(_orders, this.AddOrder, null, null);
}
还有其他一些调用WrappedCollection
构造函数的方法例如,覆盖添加但保持删除和清除正常
private WrappedListCollection<Order> _orders = new WrappedListCollection(this.AddOrder, (Order o) => _orders.RemoveFromInnerCollection(o), () => _orders.ClearInnerCollection());
我同意,如果 EF 不要求公开集合,那将是最好的,但此解决方案允许我控制对我的集合的修改。
对于阻止访问集合进行查询的问题,您可以使用上面的方法 2) 并将 WrappedCollectionGetEnumerator
方法设置为抛出NotSupportedException
. 然后你的GetOrder
方法可以保持原样。然而,一种更简洁的方法可能是公开包装的集合。例如:
public class WrappedCollection<T> : WrappedCollectionBase<T>
{
public ICollection<T> InnerCollection { get; private set; }
public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.InnerCollection = collectionToWrap;
}
protected override ICollection<T> GetWrappedCollection()
{
return this.InnerCollection;
}
}
然后方法中的调用GetOrder
将变为
_orders.InnerCollection.Where(x => x.Id == id).Single();