2012-06-25 1 views
18

Рассмотрите домен, в котором у Клиента, Компании, Сотрудника и т. Д. И т. Д. Есть свойство ContactInfo, которое, в свою очередь, содержит набор адресов, Телефон (ы), Электронный адрес (ы) и т. Д. и т.д ...Entity Framework только для чтения

Вот мое сокращенное ContactInfo:

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

Некоторые ноты (я не уверен на 100%, если это EF "лучшие практики"):

  • коллекция Адрес (es) является виртуальным для обеспечения ленивой загрузки
  • частных сеттер по сбору запрещают ЗАМЕНЫ сборов
  • коллекции является ISet, чтобы убедиться, что там нет повторяющихся адресов на контакт
  • используя AddAddress метод, который я могу гарантировать, что всегда есть и в наиболее 1-адрес, который является основным ... .

Я хотел бы (если это возможно), чтобы предотвратить добавление адресов с помощью ContactInfo.Addresses.Add() метода и заставить использование ContactInfo.AddAddress(Address address) ...

Я имею в виду, обнажая набор адресов через ReadOnlyCollection, но будет ли это работать с Entity Framework (v5)?

Как бы я это сделал?

ответ

3

Один из способов заключается в том, чтобы защитить свойство ICollection и создать новое свойство IEnumerable, которое просто возвращает список свойств ICollection.

Недостатком этого является то, что вы не можете запрашивать адреса через ContactInfo, как получить все контакты, которые проживают в этом городе.

Это не возможно !:

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

Код:

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

Нельзя ли отличать IEnumerable от ISet и делать всю магию, которая должна быть скрыта впоследствии? –

+0

@Michael К сожалению, это не вариант для меня ... Мне нужно иметь возможность запросить ... – zam6ak

+1

Вначале он не работает в коде. EF не создает таблицы для защищенного набора/набора. – 101V

14

Другой вариант, предложенный Эдо ван Asseldonk является создание пользовательской коллекции, наследующий его поведение из коллекции.

Вам нужно будет сделать свою собственную реализацию для ISet, но принцип тот же.

Скрывая любые методы, которые изменяют список и маркируют их как устаревшие, вы эффективно получаете ReadOnlyCollection, но EF все равно сможет изменить его, когда он распакован как Collection.В моей версии я добавил неявное преобразование оператора для списка, поэтому мы не должны распаковывать коллекцию при добавлении элементов:

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

Где

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

и вот 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; 
    } 
} 

Таким образом, вы все равно можете запустить Linq как обычно, но получите правильное предупреждение об использовании при попытке изменить свойство Collection. Уничтожение его в сборнике было бы единственным способом:

((Collection<MyEntity>)ListProperty).Add(entity); 
Смежные вопросы