For my company's application, I solved this problem in the following way:
1. All of our entities implement an interface, IEntity, that defines a PropertyChanging and PropertyChanged event.
2. All of our entities with many-to-one properties are strongly typed collections (we could have used generics for this, but we used a similar approach inherited from our .NET 1.1 base collections). Our collection classes raise events like ItemAdding, ItemAdded, ItemRemoving and ItemRemoved. We use field access on the collection properties (actually all properties), so NHibernate doesn't know or care about the actual type of the public colleciton property.
3. Our base entity interface defines a Strategy property, of type IEntityStrategy.
4. All of our entities are generated by reverse-engineering from existing database schema. Base classes for the strategies are also generated, which internally subscribe to the served entity's PropertyChanged and PropertyChanging events, as well as all the collection events (ItemAdding, etc) of each collection property. (The public, typed collection instances themselves are instantiated when the entity is instantiated; the entity's reference to them cannot be reassigned.) The entity classes and strategy base classes are never hand-modified. This guarantees that all of the events for property-change and collection-item-add/change/delete events will be fired at the appropriate time.
5. The generated strategy base classes expose protected methods like OnProperty1Changing, OnProperty1Changed, OnProperty2Changing, etc. They also expose protected methods like OnItem1Adding, OnItem1Added, OnItem1Changing, etc where Item1 is the singular name for the collection property named Items1. E.g. if a Person entity has different collection properties holding the same item type, such as LandLinePhones and CellPhones (assume each held the same type, Phone), that's OK, because the strategy base class will end up with generated methods called OnLandLinePhoneAdding, OnLandLinePhoneChanging, OnCellPhoneAdding, etc. The entity strategy base classes do all the event subscribing when they are created (their constructors require a reference to the entity instance they're serving).
6. Our entity factory, and our NHibernate interceptor, attach new strategy instances to each entity created.
7. All hand-coded entity logic (validation, setting defaults for new transient entities, reacting to property and collection item changes, etc) goes into the entity strategy classes. The generation process creates stubs for these that inherit from the generated strategy bases that do all the event wire-up. (NOTE: the entity generation process can be re-run at any time to pick up new tables & columns in the databas schema. Entities and strategy bases are always regenerated, but strategy stubs are only generated if they do not yet exist. We don't want precious hand-written logic to get clobbered!)
So, to conclude, our approach does have parent entities being notified when their children change, regardless of whether the child has a reference back to the parent, by way of the parent (automatically) subscribing to the child's events. However, the approach (at least the reverse-engineering from database schema) assumes one-table-per-class, with no real polymorphism. We don't have any cases where a child can have parents of different types.
Some might complain that way too many OnXXX methods are generated for the strategy base classes, and always called, while few will actually be overriden in the hand-codable strategies that descend from them. However, at least for our application, almost all data modification is performed interactively by users, not by massive background transaction processing or data loading. The simplicity in not having to fuss with event hookup is worth it for us and our customers who are not that tech-savvy yet usually need to customize our application.
|