2012-06-25 11 views
18

Considera un dominio in cui un cliente, una società, un dipendente, ecc., Hanno una proprietà ContactInfo che a sua volta contiene un insieme di indirizzi, telefoni, e-mail, ecc. , ecc ...Entity Framework read only collections

Ecco la mia ContactInfo abbreviato:

public class ContactInfo : Entity<int> 
{ 
    public ContactInfo() 
    { 
     Addresses = new HashSet<Address>();   
    } 

    public virtual ISet<Address> Addresses { get ; private set; } 

    public Address PrimaryAddress 
    { 
     get { return Addresses.FirstOrDefault(a => a.IsPrimary); } 
    } 

    public bool AddAddress(Address address) 
    { 
     // insure there is only one primary address in collection 
     if (address.IsPrimary) 
     {     
      if (PrimaryAddress != null) 
      { 
       PrimaryAddress.IsPrimary = false; 
      } 
     } 
     else 
     { 
      // make sure the only address in collection is primary 
      if (!Addresses.Any()) 
      { 
       address.IsPrimary = true; 
      } 
     } 
     return Addresses.Add(address); 
    } 
} 

Alcune note (non sono sicuro al 100% se si tratta di EF "buone pratiche"):

  • collezione di Indirizzo (es) è virtuale per consentire il caricamento lazy
  • setter privata sulla raccolta vieta la sostituzione collezione
  • collezione è un ISet per assicurare che non ci sono indirizzi duplicati per contatto
  • utilizzando AddAddress metodo che può assicurare che c'è sempre e al massimo 1 indirizzo che è primario ... .

vorrei (se possibile) per impedire l'aggiunta di indirizzi tramite ContactInfo.Addresses.Add() metodo e per forzare utilizzando dei ContactInfo.AddAddress(Address address) ...

sto pensando di esporre il di indirizzi tramite ReadOnlyCollection ma funzionerà con Entity Framework (v5)?

Come dovrei fare questo?

risposta

3

Un modo è rendere la proprietà ICollection protetta e creare una nuova proprietà di IEnumerable che restituisce solo l'elenco della proprietà ICollection.

Lo svantaggio di questo è che non è possibile interrogare gli indirizzi tramite ContactInfo come ottenere tutti i contactinfo che vivono in questa città.

Questo non è possibile !:

from c in ContactInfos 
where c.Addresses.Contains(x => x.City == "New York") 
select c 

Codice:

public class ContactInfo : Entity<int> 
{ 
    public ContactInfo() 
    { 
     Addresses = new HashSet<Address>();   
    } 

    protected virtual ISet<Address> AddressesCollection { get ; private set; } 

    public IEnumerable<Address> Addresses { get { return AddressesCollection; }} 

    public Address PrimaryAddress 
    { 
     get { return Addresses.FirstOrDefault(a => a.IsPrimary); } 
    } 

    public bool AddAddress(Address address) 
    { 
     // insure there is only one primary address in collection 
     if (address.IsPrimary) 
     {     
      if (PrimaryAddress != null) 
      { 
       PrimaryAddress.IsPrimary = false; 
      } 
     } 
     else 
     { 
      // make sure the only address in collection is primary 
      if (!Addresses.Any()) 
      { 
       address.IsPrimary = true; 
      } 
     } 
     return Addresses.Add(address); 
    } 
} 
+0

Non dovrebbe essere possibile eseguire il cast dell'IEnumerable su un ISet e fare tutta la magia che dovrebbe essere nascosta in seguito? –

+0

@Michael Purtroppo, questa non è un'opzione per me ... Devo essere in grado di interrogare ... – zam6ak

+1

Non funziona prima nel codice. EF non crea alcuna tabella per la raccolta/il set protetto. – 101V

14

Un'altra opzione suggerita da Edo van Asseldonk è quello di creare una raccolta personalizzata che eredita il suo comportamento da collezione.

Si dovrebbe fare la propria implementazione per ISet ma il principio è lo stesso.

Nascondendo tutti i metodi che modificano l'elenco e contrassegnandoli come obsoleti si ottiene effettivamente un oggetto ReadOnlyCollection ma EF sarà ancora in grado di modificarlo quando è unboxed come Collection.Nella mia versione ho aggiunto una conversione implicita operatore per List in modo che non abbiamo di unboxing della collezione quando si aggiungono elementi:

var list = ListProperty.ToList(); 
list.Add(entity) 
ListProperty = list; 

Dove

public virtual EntityCollection<MyEntity> ListProperty { get; protected set; } 

ed ecco l'EntityCollection:

public class EntityCollection<T> : Collection<T> 
{ 
    [Obsolete("Unboxing this collection is only allowed in the declarating class.", true)] 
    public new void Add(T item) { } 

    [Obsolete("Unboxing this collection is only allowed in the declarating class.", true)] 
    public new void Clear() { } 

    [Obsolete("Unboxing this collection is only allowed in the declarating class.", true)] 
    public new void Insert(int index, T item) { } 

    [Obsolete("Unboxing this collection is only allowed in the declarating class.", true)] 
    public new void Remove(T item) { } 

    [Obsolete("Unboxing this collection is only allowed in the declarating class.", true)] 
    public new void RemoveAt(int index) { } 

    public static implicit operator EntityCollection<T>(List<T> source) 
    { 
     var target = new EntityCollection<T>(); 
     foreach (var item in source) 
      ((Collection<T>) target).Add(item); // unbox 

     return target; 
    } 
} 

in questo modo è ancora possibile eseguire il Linq come al solito, ma sarà possibile ottenere una corretta avvertimento di utilizzo quando si cerca di modificare la proprietà Collection. Un-boxing su una collezione sarebbe l'unico modo:

((Collection<MyEntity>)ListProperty).Add(entity);