19

考虑一个域,其中客户、公司、员工等具有 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但这是否适用于实体框架(v5)?

我该怎么办?

4

2 回答 2

14

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 将是唯一的方法:

((Collection<MyEntity>)ListProperty).Add(entity);
于 2013-02-05T14:52:40.157 回答
3

一种方法是使 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);
    }
}
于 2012-06-26T12:07:55.030 回答