Подтвердить что ты не робот

Только для групп 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)?

Как я могу это сделать?

4b9b3361

Ответ 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);