Ho scoperto che qualsiasi cosa fosse stata eseguita, EF richiede che lo ICollection<T>
sia pubblico. Penso che questo sia dovuto al fatto che quando gli oggetti vengono caricati dal database, la mappatura cerca una proprietà di raccolta, ottiene la raccolta e quindi chiama il metodo Add
della raccolta per aggiungere ciascuno degli oggetti figlio.
Volevo assicurarmi che l'aggiunta fosse fatta attraverso un metodo sull'oggetto padre, così ho creato una soluzione di avvolgere la collezione, catturare l'aggiunta e indirizzarla al mio metodo preferito di aggiunta.
Estendere un e altri tipi di raccolta non era possibile perché il metodo Add
non è virtuale. Un'opzione è di estendere la classe Collection
e sovrascrivere il metodo InsertItem
.
ho focalizzato solo sulle Add
, Remove
, e Clear
funzioni dell'interfaccia ICollection<T>
come questi sono quelli che possono modificare la raccolta.
In primo luogo, è il mio involucro di raccolta di base che implementa l'interfaccia ICollection<T>
Il comportamento predefinito è quello di una raccolta normale. Tuttavia, il chiamante può specificare un metodo alternativo Add
da chiamare. Inoltre, il chiamante può far rispettare le operazioni Add
, Remove
, Clear
impostando le alternative su null
. Ciò comporta il lancio di NotSupportedException
se qualcuno tenta di utilizzare il metodo.
Il lancio di un'eccezione non è buono come impedire l'accesso in primo luogo. Tuttavia, il codice dovrebbe essere testato (unità testata) e un'eccezione verrà trovata molto rapidamente e verrà apportata una modifica del codice adeguata.
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();
}
}
Dato che la classe base possiamo usarla in due modi. Gli esempi stanno usando gli oggetti post originali.
1) Creare un tipo specifico di raccolta avvolto (ad esempio, List
) WrappedListCollection public class: WrappedCollectionBase, IList { privata Lista 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
}
Questo può quindi essere utilizzato:
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) Dare una collezione ad essere avvolto utilizzando
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;
}
}
che può essere utilizzato come segue:
{ pubblico Ordini di ICollection {get {return _wrappedOrders; }} // Metodi pubblici.
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);
}
Non ci sono altri modi per chiamare i WrappedCollection
costruttori Ad esempio, per eseguire l'override aggiungere ma mantenere remove e chiaro come normale
private WrappedListCollection<Order> _orders = new WrappedListCollection(this.AddOrder, (Order o) => _orders.RemoveFromInnerCollection(o),() => _orders.ClearInnerCollection());
Sono d'accordo che sarebbe stato meglio se EF non richiederebbe la raccolta è pubblica ma questa soluzione mi consente di controllare la modifica della mia collezione.
Per il problema di impedire l'accesso alla raccolta per l'interrogazione è possibile utilizzare l'approccio 2) sopra e impostare il metodo WrappedCollection GetEnumerator
per generare un NotSupportedException
. Quindi il tuo metodo GetOrder
può rimanere così com'è. Tuttavia, un metodo più ordinato potrebbe essere quello di esporre la raccolta incartata. Per esempio:
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;
}
}
Poi la chiamata nel metodo GetOrder
sarebbe diventato
_orders.InnerCollection.Where(x => x.Id == id).Single();
Se ho impostato il modificatore di accesso della mia collezione a nulla, ma pubblico non è la lettura dei valori. Un semplice passaggio da "protetta" torna a "pubblico", per esempio, rende tutto il lavoro. Inoltre, non sembra mappare le proprietà non pubbliche semplici (ad esempio stringa). – dommer
Un altro approccio consiste nell'aggiungere un file T4 e modificarlo per esporre la proprietà ap di IEnumerable per le raccolte e rendere protetta la proprietà predefinita di ICollection. –
Michael