【问题标题】:Entity Framework read only collections实体框架只读集合
【发布时间】:2013-07-09 02:45:14
【问题描述】:

考虑一个域,其中客户、公司、员工等具有 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);
    }
}

一些注意事项(我不确定这些是否是 EF“最佳实践”):

  • 地址集合是虚拟的以允许延迟加载
  • 集合的私有设置器禁止集合替换
  • 集合是ISet,以确保每个联系人没有重复的地址
  • 使用AddAddress 方法,我可以确保始终且最多有 1 个地址是主地址....

我希望(如果可能)阻止通过ContactInfo.Addresses.Add() 方法添加地址并强制使用ContactInfo.AddAddress(Address address)...

我正在考虑通过 ReadOnlyCollection 公开地址集,但这是否适用于 Entity Framework (v5)?

我该怎么办?

【问题讨论】:

    标签: c# entity-framework ef-code-first readonly-collection


    【解决方案1】:

    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 属性时会收到正确的使用警告。将它拆箱到一个集合是唯一的方法:

    ((Collection<MyEntity>)ListProperty).Add(entity);
    

    【讨论】:

      【解决方案2】:

      一种方法是保护 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);
          }
      }
      

      【讨论】:

      • 难道不能将 IEnumerable 转换为 ISet 并执行所有应该在之后隐藏的魔法吗?
      • @Michael 不幸的是,这不是我的选择...我需要能够查询...
      • 它首先在代码中不起作用。 EF 不会为受保护的集合/集合创建任何表。
      猜你喜欢
      • 1970-01-01
      • 2018-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多