为什么实体框架需要使用ICollection来进行延迟加载?

28

我想编写一个类似于以下的富领域类:

public class Product    
{    
   public IEnumerable<Photo> Photos {get; private set;}    
   public void AddPhoto(){...}    
   public void RemovePhoto(){...}
 }

但是使用实体框架(V4 Code First方法)需要使用ICollection类型进行延迟加载!由于客户端可以绕过AddPhoto / RemovePhoto方法并直接调用ICollection上的add方法,因此上述代码不再按设计工作。这样做是不好的。

public class Product    
{    
   public ICollection<Photo> Photos {get; private set;} //Bad    
   public void AddPhoto(){...}    
   public void RemovePhoto(){...}    
 }

使用EF4实现DDD变得非常令人沮丧。为什么他们选择ICollection进行延迟加载?

我该如何克服这个问题?NHibernate能否提供更好的DDD体验?


我正在尝试自己解决这个问题。我知道你描述的方法是正确的,但是怎么做呢?同时,EF 4.1现在是否支持这种方法?或者你已经找到了解决方案吗? - Rushino
3个回答

21

我认为我找到了解决方案...详见此处:http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/47296641-0426-49c2-b048-bf890c6d6af2/

你需要将ICollection类型设置为受保护,并使用它作为公共IEnumerable的后备集合。

public class Product
{

   // This is a mapped property
   protected virtual ICollection<Photo> _photos { get; set; }

   // This is an un-mapped property that just wraps _photos
   public IEnumerable<Photo> Photos
   {
      get  { return _photos; }
   }

   public void AddPhoto(){...}
   public void RemovePhoto(){...}

} 
为了让懒加载生效,类型必须实现ICollection接口,并且访问权限必须是public或protected。

4
顺便提一下,NHibernate 要求您使用实现了 ICollection 接口的 IList,因此在 NHibernate 中也需要使用相同的技巧。 - Kevin Berridge
这是答案的解决方案吗? - Rushino
你赢得了悬赏奖金。 :) 祝你愉快! - Rushino
1
同@hazzik所说。如果您使用存储库或仅尝试编写将由EF转换为SQL语句的LINQ表达式,并尝试使用public IEnumerable,则不起作用...该属性未映射,而LINQ to Entities不支持该属性。 - Thiago Silva
@Kevin - IEnumerable在NHibernate中可以很好地工作,集合可以映射到私有后备字段以实现所需的结果。查询针对公共IEnmerable {get}执行。 - Mike Rowley

6

你不能向IEnumerable中插入数据,这适用于EF和客户端。你不一定要使用ICollection,你可以使用IList或其他可写类型。我的建议是向客户端公开DTO而不是实体,以获得最佳效果。


我知道这是一个旧帖子,但您能详细说明一下这个答案吗? - Rushino
@Rushino:你的公共接口是一份合同。如果第三方与之集成,那么它就是不可变的,你无法更改它。但是你可能需要发展你的数据库,对吗?你的EF模型基本上与你的数据库匹配,所以你也需要发展它。所以请遵循单一职责原则:使用一个类型,即DTO,来定义你的公共接口。使用另一个类型,实体,来处理将DB映射到可以/可能更改的模式中。 - Craig Stuntz
所以如果我理解正确的话。你已经创建了一个对象,它是你员工的演示模型?你的员工实体在哪里?我相信这是由你的数据库生成的那个?所以从我的理解来看,你没有使用自己的实体。你是从数据库中生成它们的。 - Rushino
这是一个很好的方法,Craig。但有一件事我仍然无法弄清楚,那就是如何处理编辑和创建实体的场景。如果你创建了一个 EditViewModel,你该如何将其映射回一个实体实例,并保存到数据库中呢? - nacho10f
显示剩余4条评论

2
您可以通过使用 ReadOnlyCollection(Of T) 来克服这个问题。
public class Product    
{  
    private IList<Photo> _photos;  
    public IList<Photo> Photos {
        get
        {
            return _photos.AsReadOnly();
        }
        private set { _photos = value; }
    }
    public void AddPhoto(){...}    
    public void RemovePhoto(){...}    
}

编辑: ICollection<T> => IList<T>

希望这是您想要的。


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