Entity Framework read only collections

后端 未结 2 754
北恋
北恋 2020-12-01 11:09

Consider a domain where a Customer, Company, Employee, etc, etc, have a ContactInfo property which in turn contains a set of Address(es), Phone(es), Email(s), etc, etc...

相关标签:
2条回答
  • 2020-12-01 11:23

    Another option suggested by Edo van Asseldonk is to create a custom collection that inherits its behavior from Collection.

    You'd have to make your own implementation for ISet but the principle is the same.

    By hiding any methods that modifies the list and marking them as obsolete you effectively get a ReadOnlyCollection but EF will still be able to modify it when it's unboxed as Collection. In my version I've added an implicit operator conversion for List so we don't have to unbox the collection when adding items:

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

    Where

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

    and here's the 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;
        }
    }
    

    This way you can still run your Linq as usual but will get a proper warning of usage when trying to modify the Collection property. Un-boxing it to a Collection would be the only way:

    ((Collection<MyEntity>)ListProperty).Add(entity);
    
    0 讨论(0)
  • 2020-12-01 11:48

    One way is to make the ICollection property protected and create a new property of IEnumerable that just returns the list of the ICollection property.

    The downside with this is that you are not able to query on addresses through the ContactInfo like get all contactinfo that lives in this city.

    This is not possible!:

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

    Code:

    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 讨论(0)
提交回复
热议问题