Entity Framework 只读集合

19
考虑一个领域,其中客户、公司、员工等都有“联系信息”属性,该属性包含一组地址、电话、电子邮件等等。
这是我的简化联系信息:
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的最佳实践):

  • 地址集合(collection of Address(es))是虚拟的,以实现延迟加载。
  • 集合的私有设置器(private setter on collection)禁止替换集合。
  • 集合是一个ISet,以确保每个联系人没有重复的地址。
  • 使用AddAddress方法,我可以确保始终至多只有一个主要地址....

如果可能的话,我想防止通过ContactInfo.Addresses.Add()方法添加地址,并强制使用ContactInfo.AddAddress(Address address)方法...

我在考虑通过ReadOnlyCollection来公开地址集合,但这样做对于Entity Framework(v5)是否可行?

如何去做呢?

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

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

难道不应该将IEnumerable转换为ISet并执行所有应该隐藏的操作吗? - Dominik Kirschenhofer
@Michael 很遗憾,这对我来说不是一个选项...我需要能够查询... - zam6ak
1
它在Code First中无法工作。EF不会为受保护的集合/集创建任何表。 - 101V

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接