Why does the entity framework need an ICollection

2019-01-09 01:02发布

问题:

I want to write a rich domain class such as

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

But the entity framework (V4 code first approach) requires an ICollection type for lazy loading! The above code no longer works as designed since clients can bypass the AddPhoto / RemovePhoto method and directly call the add method on ICollection. This is not good.

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

It's getting really frustrating trying to implement DDD with the EF4. Why did they choose the ICollection for lazy loading?

How can i overcome this? Does NHibernate offer me a better DDD experience?

回答1:

I think i found the solution...See here for more details: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/47296641-0426-49c2-b048-bf890c6d6af2/

Essentially you want to make the ICollection type protected and use this as the backing collection for the public 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(){...}

} 

For lazy loading to work the type must implement ICollection and the access must be public or protected.



回答2:

You can't insert into an IEnumerable. This applies to the EF just as much as it does to your clients. You don't have to use ICollection, though; you can use IList or other writeable types. My advice to get the best of both worlds is to expose DTOs rather than entities to your clients.



回答3:

You can overcome this by using the 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(){...}    
}

EDIT: ICollection<T> => IList<T>

Hope that is what you were looking for.