C# 添加到导航属性时验证实体

C# 添加到导航属性时验证实体,c#,entity-framework,validation,collections,C#,Entity Framework,Validation,Collections,我有一个具有集合属性的实体,其外观如下: public class MyEntity { public virtual ICollection<OtherEntity> Others { get; set; } } public void AddOther(OtherEntity other) { // perform validation code here this.Others.Add(other); } public class MyEntity

我有一个具有集合属性的实体,其外观如下:

public class MyEntity
{
    public virtual ICollection<OtherEntity> Others { get; set; }
}
public void AddOther(OtherEntity other)
{
    // perform validation code here

    this.Others.Add(other);
}
public class MyEntity
{
    private readonly ICollection<OtherEntity> _others = new Collection<OtherEntity>();

    public virtual IEnumerable<OtherEntity>
    {
        get
        {
            return _others.AsEnumerable();
        }
    }
}
public interface IValidatableEntity
{
    void Validate();
}
到目前为止,我已经测试了一些东西,我最终得出的结论是这样的。我在我的实体上创建了一个
private
集合,并公开了一个
public ReadOnlyCollection
因此
MyEntity
如下所示:

public class MyEntity
{
    public virtual ICollection<OtherEntity> Others { get; set; }
}
public void AddOther(OtherEntity other)
{
    // perform validation code here

    this.Others.Add(other);
}
public class MyEntity
{
    private readonly ICollection<OtherEntity> _others = new Collection<OtherEntity>();

    public virtual IEnumerable<OtherEntity>
    {
        get
        {
            return _others.AsEnumerable();
        }
    }
}
public interface IValidatableEntity
{
    void Validate();
}
公共类MyEntity
{
私有只读ICollection_others=新集合();
公共虚拟可数
{
得到
{
返回_others.AsEnumerable();
}
}
}
这个似乎是我正在寻找的,我的单元测试通过得很好,但我还没有开始做任何集成测试,所以我想知道:

  • 有没有更好的方法来实现我的目标
  • 如果我决定走这条路(如果可行),我将面临什么样的影响
  • 谢谢你的帮助

    编辑1我已从使用
    ReadOnlyCollection
    更改为
    IEnumerable
    并使用
    return\u others.AsEnumerable()作为我的getter。单元测试再次顺利通过,但我不确定在集成过程中会遇到什么问题,EF开始用相关实体构建这些集合


    编辑2因此,我决定尝试创建派生集合(称之为
    ValidatableCollection
    )的建议,实现
    ICollection
    ,其中我的
    .Add()
    方法将在将实体添加到内部集合之前对其执行验证。不幸的是,Entity Framework在构建导航属性时调用了此方法-因此它并不真正合适。

    我将创建集合类来实现此目的:

    OtherEntityCollection : Collection<OtherEntity>
    {
        protected override void InsertItem(int index, OtherEntity item)
        {
            // do your validation here
            base.InsertItem(index, item);
        }
    
        // other overrides
    }
    
    OtherEntityCollection:集合
    {
    受保护的重写void插入项(int索引,OtherEntity项)
    {
    //在这里进行验证
    基本插入项(索引,项目);
    }
    //其他覆盖
    }
    
    这将使其更加严格,因为无法绕过此验证。您可以在中检查更复杂的示例

    我不确定的一件事是,当EF从数据库中具体化数据时,如何让它创建这个具体类型。但这可能是可行的

    编辑: 如果希望将验证保留在实体内部,可以通过实体将实现的自定义接口和调用此接口的通用集合使其成为通用的


    至于EF的问题,我认为最大的问题是,当EF重新物质化集合时,它会为每个项目调用
    Add
    。然后调用验证,即使该项不是作为业务规则而是作为基础结构行为“添加”的。这可能会导致奇怪的行为和bug。

    我创建collection类正是为了这个目的:

    OtherEntityCollection : Collection<OtherEntity>
    {
        protected override void InsertItem(int index, OtherEntity item)
        {
            // do your validation here
            base.InsertItem(index, item);
        }
    
        // other overrides
    }
    
    OtherEntityCollection:集合
    {
    受保护的重写void插入项(int索引,OtherEntity项)
    {
    //在这里进行验证
    基本插入项(索引,项目);
    }
    //其他覆盖
    }
    
    这将使其更加严格,因为无法绕过此验证。您可以在中检查更复杂的示例

    我不确定的一件事是,当EF从数据库中具体化数据时,如何让它创建这个具体类型。但这可能是可行的

    编辑: 如果希望将验证保留在实体内部,可以通过实体将实现的自定义接口和调用此接口的通用集合使其成为通用的


    至于EF的问题,我认为最大的问题是,当EF重新物质化集合时,它会为每个项目调用
    Add
    。然后调用验证,即使该项不是作为业务规则而是作为基础结构行为“添加”的。这可能会导致奇怪的行为和错误。

    我建议返回到
    ReadOnlyCollection
    。我过去用过它,我没有任何问题

    此外,
    AsEnumerable()
    方法将不起作用,因为它只更改引用的类型,不会生成新的独立对象,这意味着

    MyEntity m = new MyEntity();
    Console.WriteLine(m.Others.Count()); //0
    (m.Others as Collection<OtherEntity>).Add(new OtherEntity{ID = 1});
    Console.WriteLine(m.Others.Count()); //1
    
    MyEntity m=newmyentity();
    Console.WriteLine(m.Others.Count())//0
    添加(新的OtherEntity{ID=1});
    Console.WriteLine(m.Others.Count())//1.
    

    将成功插入您的私人收藏。

    我建议返回到
    ReadOnlyCollection
    。我过去用过它,我没有任何问题

    此外,
    AsEnumerable()
    方法将不起作用,因为它只更改引用的类型,不会生成新的独立对象,这意味着

    MyEntity m = new MyEntity();
    Console.WriteLine(m.Others.Count()); //0
    (m.Others as Collection<OtherEntity>).Add(new OtherEntity{ID = 1});
    Console.WriteLine(m.Others.Count()); //1
    
    MyEntity m=newmyentity();
    Console.WriteLine(m.Others.Count())//0
    添加(新的OtherEntity{ID=1});
    Console.WriteLine(m.Others.Count())//1.
    

    将成功插入到您的私人收藏中。

    您不应该在
    HashSet
    上使用
    AsEnumerable()
    ,因为可以通过将收藏强制转换为
    ICollection
    来轻松修改收藏

    var values=new MyEntity().Entities;
    ((ICollection)值)。添加(新的OtherEntity());
    
    尝试返回列表的副本,如

    return new ReadOnlyCollection<OtherEntity>(_others.ToList()).AsEnumerable();
    
    返回新的只读集合(_others.ToList()).AsEnumerable();
    
    这确保用户在试图修改异常时会收到异常。您可以将
    ReadOnlyCollection
    公开为返回类型,而不是
    IEnumerable
    以清晰方便用户。在.NET 4.5中添加了一个新接口
    IReadOnlyCollection


    除了一些依赖列表的组件外,您不会有大的集成问题。如果用户调用ToList或ToArray,他们将返回一个副本

    您不应该在
    哈希集上使用
    AsEnumerable()