27

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?

  • Trying to figure out this myself. I know the way to go is the way you described. But how ? Also it is possible in EF 4.1 now ? or did you found a solution ? – Rushino Sep 14 '11 at 13:38

3 Answers3

21

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.

Ekk
  • 525
  • 1
  • 3
  • 6
  • 4
    As an FYI, NHibernate requires you to use IList, which implements ICollection, so you'd have to use this same trick with NHibernate. – Kevin Berridge Jul 12 '10 at 16:07
  • Was this the solution to this answer ? – Rushino Sep 14 '11 at 13:37
  • You won the bounty. :) Enjoy! – Rushino Sep 21 '11 at 23:36
  • 1
    ditto to @hazzik. If you use repositories or just try to write a LINQ expression that will be converted by EF into a SQL statement, and try to use the public IEnumerable, it won't work....that property is not mapped and LINQ to Entities doesn't support that. – Thiago Silva May 23 '13 at 16:10
  • @Kevin - IEnumerable works fine with NHibernate and the collection can be mapped to a private backing field to achieve the desired result. Queries go against the public IEnmerable {get} – Mike Rowley Jul 16 '13 at 01:19
5

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.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
  • So true. Exposing DTOs and optionally generating view models from those DTOs is the path I choose. I think too many people get scared away but how much more code it sounds like they will have to write to accomplish it. – Joseph Ferris May 19 '10 at 21:27
  • I know this is an old post but could you elaborate on this answer ? – Rushino Sep 14 '11 at 13:37
  • @Rushino: Your public interface is a contract. If third parties integrate with it, it's immutable and you can't change it. But you might want to evolve your DB, right? Your EF model matches your DB mostly, so you'll want to evolve that, too. So use the single responsibility principal: Use one type, a DTO, to define your public interface. Use a different type, an entity, to handle DB mapping to a schema which can/might change. – Craig Stuntz Sep 14 '11 at 13:46
  • Do you have an example ? i am pretty visual and i belive this would help others. – Rushino Sep 14 '11 at 13:48
  • 1
    @Rushino: [An example of projecting onto a POCO? Sure.](http://blogs.teamb.com/craigstuntz/2009/12/31/38500/) – Craig Stuntz Sep 14 '11 at 13:49
  • So if i understand correctly. Youve made an object which is a presentation model of your employee ? Where is your employee entity ? I believe this is the one generated by your database ? so from what i understand your not using your own entities. You generate them from the DB. – Rushino Sep 14 '11 at 16:08
  • @Rushino: The "entity" is `e` in that example. The important point here is that an object of type `Employee` was *never even materialized* in this query. The entity, in this context, serves solely for mapping in your LINQ query, not for actual instantiation. – Craig Stuntz Sep 14 '11 at 16:17
  • this is a great approach Craig, one thing I still cant figure out though is how to handle scenarios of editing and creating entities. if you create a EditViewModel how do you handle mapping that back to a entity instance and then save to database? – nacho10f Sep 19 '11 at 16:32
  • @NachoF: I write code for that. Usually updating the DB is one area where I want as little "magic" as possible. – Craig Stuntz Sep 19 '11 at 18:05
2

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.

Community
  • 1
  • 1
Tri Q Tran
  • 5,500
  • 2
  • 37
  • 58