2012-06-25 30 views
18

Hãy xem xét một miền nơi Khách hàng, Công ty, Nhân viên, v.v., có thuộc tính ContactInfo, lần lượt chứa một tập hợp Địa chỉ, Điện thoại, Email, v.v. , vv ...Entity Framework chỉ đọc các bộ sưu tập

đây là ContactInfo tôi viết tắt:

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

Một số lưu ý (tôi không chắc chắn 100% nếu đây là những EF "thực hành tốt nhất"):

  • thu Địa chỉ (es) là ảo để cho phép tải chậm
  • setter tin về thu cấm thay thế bộ sưu tập
  • bộ sưu tập là một ISet để đảm bảo rằng không có địa chỉ trùng lặp mỗi xúc
  • sử dụng AddAddress phương pháp tôi có thể đảm bảo rằng luôn luôn và ở hầu hết có 1 địa chỉ đó là chính ... .

tôi muốn (nếu có thể) để ngăn chặn việc thêm địa chỉ qua ContactInfo.Addresses.Add() phương pháp và buộc sử dụng của ContactInfo.AddAddress(Address address) ...

tôi đang nghĩ đến phơi bày bộ địa chỉ qua ReadOnlyCollection nhưng điều này có hoạt động với Entity Framework (v5) không?

Tôi sẽ làm như thế nào?

Trả lời

3

Một cách là làm cho thuộc tính ICollection được bảo vệ và tạo thuộc tính mới của IEnumerable, chỉ trả về danh sách thuộc tính ICollection.

Nhược điểm với điều này là bạn không thể truy vấn địa chỉ thông qua ContactInfo như nhận được tất cả contactinfo đang sống trong thành phố này.

này là không thể !:

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

Không nên đưa IEnumerable đến một ISet và làm tất cả các phép thuật cần được ẩn sau đó? –

+0

@Michael Thật không may, đây không phải là một lựa chọn cho tôi ... Tôi cần để có thể truy vấn ... – zam6ak

+1

Nó không hoạt động trong mã đầu tiên. EF không tạo bất kỳ bảng nào cho bộ sưu tập/bộ sưu tập được bảo vệ. – 101V

14

Một tùy chọn khác được đề xuất bởi Edo van Asseldonk là tạo ra một bộ sưu tập tùy chỉnh mà thừa hưởng hành vi của nó từ Collection.

Bạn sẽ phải thực hiện triển khai của riêng bạn cho ISet nhưng nguyên tắc là như nhau.

Bằng cách ẩn bất kỳ phương pháp nào sửa đổi danh sách và đánh dấu chúng là lỗi thời, bạn sẽ có được ReadOnlyCollection nhưng EF vẫn có thể sửa đổi nó khi nó được bỏ hộp dưới dạng Bộ sưu tập.Trong phiên bản của tôi, tôi đã thêm một chuyển đổi điều hành ngầm cho Danh sách vì vậy chúng tôi không cần phải Unbox bộ sưu tập khi thêm các mục:

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

đâu

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

và đây là 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; 
    } 
} 

Bằng cách này bạn vẫn có thể chạy LINQ như bình thường nhưng sẽ nhận được cảnh báo sử dụng thích hợp khi cố sửa đổi thuộc tính Bộ sưu tập. Un-boxing nó vào một Bộ sưu tập sẽ là cách duy nhất:

((Collection<MyEntity>)ListProperty).Add(entity); 
Các vấn đề liên quan