views:

256

answers:

2

I have a POCO domain model which is wired up to the entity framework using the new ObjectContext class.

public class Product
    {
        private ICollection<Photo> _photos;

        public Product()
        {
            _photos = new Collection<Photo>();         
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public virtual IEnumerable<Photo> Photos
        {
            get
            {
                return _photos;
            }
        }

        public void AddPhoto(Photo photo)
        {
            //Some biz logic
            //...
            _photos.Add(photo);
        }
    }

In the above example i have set the Photos collection type to IEnumerable as this will make it read only. The only way to add/remove photos is through the public methods.

The problem with this is that the Entity Framework cannot load the Photo entities into the IEnumerable collection as it's not of type ICollection.

By changing the type to ICollection will allow callers to call the Add mentod on the collection itself which is not good.

What are my options?

Edit:

I could refactor the code so it does not expose a public property for Photos:

public class Product
    {
    public Product()
    {
        Photos = new Collection<Photo>();         
    }

    public int Id { get; set; }
    public string Name { get; set; }
    private Collection<Photo> Photos {get; set; }

    public IEnumerable<Photo> GetPhotos()
    {
        return Photos; 
    }

    public void AddPhoto(Photo photo)
    {
        //Some biz logic
        //...
        Photos.Add(photo);
    }

    }

And use the GetPhotos() to return the collection. The other problem with the approach is that I will loose the change tracking abilities as I cannot mark the collection as Virtual - It is not possible to mark a property as private virtual.

In NHibernate I believe it's possible to map the proxy class to the private collection via configuration. I hope that this will become a feature of EF4. Currently i don't like the inability to have any control over the collection!

A: 

(Apologies for my initial post brevity - I was answering from my phone)

You can construct your collection through a LINQ query over an EF entity set. However, you keep the resulting collection as internal data member to your business class and expose the IEnumerable<Photo> returned by calling AsEnumerable() on the entity set as a result of the public photo.

You could cache the IEnumerable<Photos> internally as well, so that you don't call AsEnumerable() every time your caller asks for the collection. Of course, that means that if the user needs to update the collection through your public methods, you might have to refresh the cached IEnumerable. This might pose small issue if the caller has also cached the pointer to the previous IEnumerable.

Alternatively, if your caller will always work with the full entity set, the EntitySet class (of which all your EF sets will inherit) implements IEnumerable<TEntity>, so you can directly return the entity set to your caller.

Note that if you want the loading of the collection from an EF entity set to happen outside of the scope of your business class, you can make a constructor on your class that takes an ICollection. This way, once you create your object, the collection is sealed in it, and exposed only as an IEnumerable.

Franci Penov
I don't see how calling AsEnumerable works in this case? Can you explain a bit more?
`AsEnumerable()` will help in making the collection truely immutable, because without it, a caller can still do this: `((Collection<Photo>)p.Photos).Add(new Photo())`. However, it doesn't help you from being able to construct the object using EF.
Steven
AsEnumerable() assumes that the collectiondy construed through the normal means of constructing collections from EF - either through a LINQ query or by directly accessing the EntitySet.
Franci Penov
Our domain layer is separated from the data layer. I'm using the repository pattern to pass POCO objects from the data layer to our business objects. What you suggest is entirely possible just that database logic has creped into the domain / business model, which some might say is not pure POCO.
I see. In that case, I would create the POCO object (Photos in your example) with an explicit constructor that takes an ICollection. This way, your data layer will be responsible for instantiating the POCO object and loading it with the data from EF, while your POCO object will still expose the data as an IEnumerable. No client of the business object will be able to update it's collection directly. I'll update my answer with that suggestion.
Franci Penov
A: 

Why not try the following and leave use properties?

private ICollection<Photo> photos{get; set;}
public IEnumerable<Photo> Photos
{
    get {return (IEnumberable<Photo>)photos;}
}

Alternatively you could use the decorator pattern to encapsulate the class into one which the collection can't be directly modified.

Kirk