2012-06-25 11 views
18

Considere un dominio donde un Cliente, Empresa, Empleado, etc. tiene una propiedad ContactInfo que a su vez contiene un conjunto de Dirección (es), Teléfono (s), Correo electrónico (s), etc. , etc ...Colecciones de solo lectura de Entity Framework

Aquí está mi ContactInfo abreviada:

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); 
    } 
} 

Algunas notas (no estoy 100% seguro de si se trata de EF "mejores prácticas"):

  • colección de Dirección (es) es virtual para permitir la carga lenta
  • colocador privada en la recolección prohíbe la sustitución de recogida
  • colección es una ISet para asegurar que no hay direcciones duplicadas por contacto
  • utilizando AddAddress método puedo asegurar que no siempre es y como máximo 1 dirección que es primordial ... .

me gustaría (si es posible) para evitar la adición de direcciones a través de ContactInfo.Addresses.Add() método y para forzar el uso de ContactInfo.AddAddress(Address address) ...

Pienso exponer el conjunto de direcciones a través del ReadOnlyCollection, pero ¿funcionará esto con Entity Framework (v5)?

¿Cómo podría hacerlo?

Respuesta

3

Una forma es proteger la propiedad ICollection y crear una nueva propiedad de IEnumerable que simplemente devuelve la lista de la propiedad ICollection.

La desventaja de esto es que no puede consultar las direcciones a través de ContactInfo, como obtener todos los contactos que se encuentran en esta ciudad.

Esto no es posible !:

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

Código:

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

¿No debería ser posible emitir el IEnumerable a un IColoque y hacer toda la magia, que debe ser ocultado después? –

+0

@Michael Lamentablemente, esta no es una opción para mí ... Necesito poder consultar ... – zam6ak

+1

No funciona primero en el código. EF no crea ninguna tabla para la colección/conjunto protegido. – 101V

14

Otra opción sugerida por Edo van Asseldonk es crear una colección personalizada que hereda el comportamiento de la colección.

Tendría que hacer su propia implementación para ISet pero el principio es el mismo.

Al ocultar los métodos que modifican la lista y marcarlos como obsoletos, obtienes un ReadOnlyCollection pero EF aún podrá modificarlo cuando se desagrupe como Collection.En mi versión que he añadido un operador de conversión implícita para la lista, así que no tenemos que desempacar la colección al agregar elementos:

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

Dónde

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

y aquí está la 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; 
    } 
} 

De esta manera todavía puede ejecutar su Linq como de costumbre, pero recibirá una advertencia adecuada de uso cuando intente modificar la propiedad de Colección. Un-boxing a una colección sería la única manera:

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