The Elephant and the Silverlight

Thoughts and analysis of Silverlight for business applications

Page List

    Advanced EntityCollection

    Previously,I gave an overview of the EntityCollection. In this post we are going to look at how the EntityCollection actually works and how we can add our own EntityCollections to extend our client side entities.

    First, lets look at how RIA Services generates an EntityCollection during code generation

    private EntityCollection<Book> _books; 
    
    [Association("Library_Book", "LibraryId", "LibraryId")] 
    [XmlIgnore()] 
    public EntityCollection<Book> Books 
    { 
        get 
        { 
            if ((this._books == null)) 
            { 
                this._books = new EntityCollection<Book>(this, "Books", 
                              this.FilterBooks, this.AttachBooks, this.DetachBooks); 
            } 
            return this._books; 
        } 
    } 
    
    private void AttachBooks(Book entity) 
    { 
        entity.Library= this; 
    } 
    
    private void DetachBooks(Book entity) 
    { 
        entity.Library= null; 
    } 
    
    private bool FilterBooks(Book entity) 
    { 
        return (entity.LibraryId == this.LibraryId); 
    } 
    
    

     

    Lets look at that constructor, from the documentation the constructor is defined as

    public EntityCollection(
        Entity parent,
        string memberName,
        Func<TEntity, bool> entityPredicate,
        Action<TEntity> attachAction,
        Action<TEntity> detachAction
    )
    
     

    So, the generated code is creating a new instance of EntityCollection passing in the current entity as the parent, “Books” as the name of the property, and FilterBooks as the EntityCollection’s filter.

    The AttachAction and DetachAction  are the actual methods (beyond some internal plumbing) that you are calling when you call EntityCollection.Add and EntityCollection.Remove. Now that you see the actual code perhaps my earlier advice on not using Detach and Attach for attached entities makes more sense.

    The Fun Part

    Now, here is the fun part. We can actually create our own EntityCollections. In the above example we have an collection of books. What if I want multiple collections of books each filtered by the type of book? This can easily be done using custom EntityCollections.

    public sealed partial class Library 
    {
    private EntityCollection<Book> _mysteryBooks;
    [Association("Library_MysteryBook", "LibraryId", "LibraryId,Category")] [XmlIgnore()] public EntityCollection<Book> MysteryBooks { get { if ((this._mysteryBooks == null)) { this._mysteryBooks = new EntityCollection<Book>(this, "Books", this.FilterMysteryBooks); } return this._mysteryBooks; } } private bool FilterMysteryBooks(Book entity) { return (FilterBooks(entity) && entity.Category == "Mystery"); } }

    Here I am using the alternate constructor which doesn’t have attach and detach. In this case, I wanted my EntityCollection to be read only. I also called the original FilterBooks function in hopes that this would make future maintenance easier.

    Keep in mind that EntityCollection isn’t really a CollectionView. There is no external refresh method, the EntityCollection only refreshes based on the information in the attached AssociationAttribute. That means only new Books, deleted Books, or changes to LibraryFk or Category will cause the EntityCollection to refresh.


    Permalink | Comments (6) | Post RSSRSS comment feed

    Comments

    Alex Y United States

    Monday, August 31, 2009 8:29 AM

    Alex Y


    Thanks a lot for getting into this topics.

    Alex

    Mark F United Kingdom

    Monday, January 04, 2010 4:49 PM

    Mark F

    Thanks for these posts - it's making everything RIA more real.
    However a couple of small points on the above code, you've missed out the private field, _mysteryBooks, and you need to name the new EntityCollection MysteryBooks not just Books to avoid a clash with the original EntitiyCollection.
    Also a question, can you do this without a parent? For instance, if I have two libraries, American and English, can I set up an EntitiyCollection of all mystery books whether thay are US or English?

    ColinBlair United States

    Tuesday, January 05, 2010 10:38 PM

    ColinBlair

    Good eyes Mark. If you don't have a parent then there are better objects to use other then the EntityCollection. I usually use the PagedCollectionView for creating filtered views of the EntitySet.

    Nick Walker United States

    Thursday, January 28, 2010 11:21 AM

    Nick Walker

    I'm working on a RIA application where I project a presentation model to the client, and I'm having some trouble with asssociations implemented in the autogenerated code via EntityCollections.

    In my generated code, "Attach" and "Detach" actions are not generated for an EntityCollection and my generated code does not use the constructor that accepts Attach and Detach actions (I've tried using and not using IsForeignKey and [Composition] in my poresentation class definitions to see if it changed the autogenerated code, but it doesn't. I see you made this post in August, prior to the WCF Services Beta being released in November (I'm using the Beta with SL3 on VS2008), so maybe there's some difference there.

    Let me use a simple example with Artists and Albums: an Artist has multiple Albums, and an Album has one artist. If I update an Album on the client with a new Artist and SubmitChanges, the Album will be sent to the server for an Update call. The Album that shows up on the server has a null value for Artist (unless I mark the association with [Composition], but I don't want to do that here because it's not truly a compositional relationship), but that's ok, because the AlbumID value has changed and I can use that to query storage to get the right entity on the DAL side.

    What if I want to change the Albums that belong to an Artist? From what I can tell, theArtist.Albums.Add(anAlbum) and theArtist.Albums.Remove(anAlbum) do absolutely nothing. They don't mark the theArtist entity as changed, and they don't even update the collection that the client has. From the autogenerated code you are showing that has Attach and Detach actions, it looks as if the idea is that what you really need to do is update anAlbum.Artist, submit that change to the server, and then refresh the client entities. OK, I suppose that makes sense to me, but if that's the case, why am I not seeing appropriate Attach and Detach actions in my autogenerated code?

    Thanks!

    Nick Walker United States

    Thursday, January 28, 2010 12:01 PM

    Nick Walker

    Sorry to double-post, but I want to correct what I said above, after doing a bit more investigation.

    I was doing a couple of things wrong and fixed things up, and have pretty much resolved most of my issues *except* for the absense of that generated code. My problem before was that the Add and Remove methods on the EntityCollection *were* working, but I hadn't noticed Remove working initially and when I was playing around with Add I forgot that I first had to remove an Album from one artist before adding it to another. Duh.

    Anyways, Add and Remove are correctly updating the Artist EntityCollections, but the Artist property on the Album entity itself is not being changed, so context.HasChanges returns false and nothing gets updated. Bridging this gap (updating the Album's Artist when moving it between Artists' Album collections) looks to be entirely the point of those Attach and Detach actions that show up in your generated code, but not mine.

    I could always add code to make sure to change an Album's artist when moving it between collections, but the Attach and Detach action methods really seem to be the best place to put that logic. Why isn't that code being generated?

    Nick Walker United States

    Thursday, January 28, 2010 1:53 PM

    Nick Walker

    For those who stumble across this later, the solution is here: forums.silverlight.net/.../355751.aspx#355751 My problem was that I didn't know that the Association attributes on each side of the association have to have the same name for Attach and Detach methods to be generated and used.

    Add comment


    (Will show your Gravatar icon)

      Country flag

    Click to change captcha
    biuquote
    • Comment
    • Preview
    Loading