Только для групп Entity Framework
Рассмотрим домен, в котором Клиент, Компания, Сотрудник и т.д. и т.д. имеют свойство ContactInfo, которое, в свою очередь, содержит набор адресов (адресов), Телефон (ы), Электронный адрес и т.д. и т.д....
Вот мой сокращенный 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):
- коллекция адресов (адресов) виртуальна, чтобы обеспечить ленивую загрузку.
- закрытый наборщик по сборке запрещает замену коллекции
- коллекция - это
ISet
, чтобы гарантировать отсутствие дублирующих адресов для каждого контакта.
- используя метод
AddAddress
, я могу гарантировать, что всегда и не более 1 адреса, который является основным....
Я хотел бы (если возможно) предотвратить добавление адресов через метод ContactInfo.Addresses.Add()
и принудительно использовать ContactInfo.AddAddress(Address address)
...
Я собираюсь разоблачить набор адресов через ReadOnlyCollection
, но будет ли это работать с Entity Framework (v5)?
Как я могу это сделать?
Ответы
Ответ 1
Один из способов заключается в том, чтобы защитить свойство 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);
}
}
Ответ 2
Другой вариант, предложенный Edo van Asseldonk, заключается в создании пользовательской коллекции, которая наследует ее поведение от Collection.
Вам нужно будет сделать свою собственную реализацию для ISet, но принцип тот же.
Скрывая любые методы, которые изменяют список и маркируют их как устаревшие, вы действительно получаете ReadOnlyCollection, но EF все равно сможет его модифицировать, когда он будет распакован как Collection. В моей версии я добавил неявное преобразование оператора для List, поэтому нам не нужно распаковывать коллекцию при добавлении элементов:
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. Un-boxing его в Collection будет единственным способом:
((Collection<MyEntity>)ListProperty).Add(entity);