views:

43

answers:

1

If I use bidirectional associations in nHibernate, is it necessary for me to add my own code to both sides of the relationship to automatically update the other side?

For example, if I have a Many-to-One relationship between Book and Category

public class Book{
    public Category Category{ get; set; }
}

public class Category{
    public IList Books{ get; set; }
}

Do I need to add code to Book.Category's set() something like this:

private Category _category;
public Category Category {
    get { return _category;  }
    set {
        Category priorCategory = _category;
        Category newCategory = value;

        if( newCategory != priorCategory ) {
            if( priorCategory != null ) {
                _category = null;
                priorCategory.Books.Remove( this );
            }

            _category = newCategory;
            if( newCategory != null ) {
                newCategory.Books.Add( this );
            }
        }
    }
}

and also add code to Category.Books something like this so that callers can add and remove books from Category.Books (e.g., Category.Books.Add() and Category.Books.Remove())?

(handling this by wrapping the List in an ObservableCollection so I can respond to add/remove events)

private IList _books { get; set; }
public ICollection<Book> Books
{
    get{
        if( _books == null ) _books = new ArrayList( );
        var observableBooks = new ObservableCollection<Book>( _books.Cast<Book>( ) );
        observableBooks.CollectionChanged += BookCollectionChanged;
        return observableBooks;
    }
}

private void OnBookAdded( Book addedBook ) {
    _books.Add( addedBook );
    addedBook.Category = this;
}

private void OnBookRemoved( Book removedBook ) {
    _books.Remove( removedBook );
    removedBook.Category = null;
}

// this just calls OnBookAdded() and OnBookRemoved() in response to change events
private void BookCollectionChanged( object sender, NotifyCollectionChangedEventArgs e ) {
    if( NotifyCollectionChangedAction.Add == e.Action ) {
        foreach( Book addedBook in e.NewItems ) {
            OnBookAdded( addedBook );
        }
    }

    if( NotifyCollectionChangedAction.Remove == e.Action ) {
        foreach( Book removedBook in e.OldItems ) {
            OnBookRemoved( removedBook );
        }
    }
}

Or does nHibernate do any graph consistency for you that it would automatically update some of this for you?

+2  A: 

Not necessary to update both sides, but always necessary to update the "master" direction of the relationship (i.e. the one that is not mapped as "inverse"). Thus it is best practice to write code to keep the two directions in synch, and you will find it makes your life easier in the long run.

David M
Sorry - are you saying not necessary, but best practice to do so?
Abby Fichtner
I'm saying that there is, for any relationship, a direction in which it must be maintained; the other direction is not necessary to have the database updated successfully. Thus it is not necessary to do it both ways, but you do when coding have to keep in mind which direction is the "master" and therefore necessary. Thus it is definitely best practice to do what you are suggesting.
David M
How do I know which side is Master in a 1:M relationship? And why is it best practice to do it both ways if I only need to do it from one way?
Abby Fichtner
You would normally either map the collection (1=>M) as inverse, making the M=>1 the master, or you would map the property (M=>1) as not for insert or update, making the 1=>M the master. Hope that makes sense. It's best practice to do it both ways because it is, as you said, a bi-directional relationship. If you only define it one way in your code, you end up with an object graph that doesn't actually represent the true state of your objects.
David M