3
votes

I have a bi-directional, many-to-many association between EntityA and EntityB and I’m using an association class, EntityABLink, to model this because there are other attributes about the relationship that I need to track. In addition, I also have another class that holds a reference to a specific relationship between EntityA and EntityB so I treat the association class as a full-fledged entity.

In EntityA I have a read-only property that returns a list of associated EntityB objects and, likewise, in EntityB I have a read-only property that returns a list of associated EntityA objects. Note that, for these properties, I’m hiding the fact that the association is implemented via an association class. (I also have dedicated methods for updating the relationships that hide the implementation.) Behind the scenes in both EntityA and EntityB, I have private collections of type EntityABLink.

Since a picture is worth a thousand words, here is what I have described so far:
enter image description here

(Note again that the public, read-only properties on EntityA and EntityB are not of the same type as the private members that back them up.)

So far, so good. Now I want to persist these objects to a database using Fluent NHibernate automapping overrides. When it comes to mapping, I like to think of the above using this functionally equivalent representation:
enter image description here From this diagram, it’s clear that what I really need is two bi-directional one-to-many relationships.

In mapping the above, I figure that I need something like this:

In the EntityA automapping override:

mapping.HasMany<EntityABLink>(Reveal.Member<EntityA>(“_AssociationList”)).Inverse().AsBag().Cascade.SaveUpdate(); 
mapping.IgnoreProperty(x => x.EntityBList);

In the EntityB automapping override:

mapping.HasMany<EntityABLink>(Reveal.Member<EntityB>(“_AssociationList”)).Inverse().AsBag().Cascade.SaveUpdate(); 
mapping.IgnoreProperty(x => x.EntityAList);

In the EntityABLink automapping override:

mapping.References<EntityA>(x => x.EntityA).Not.Nullable();
mapping.References<EntityB>(x => x.EntityB).Not.Nullable();

When I try this, however, I get the following error:

"Could not find a getter for property '_ AssociationList’ in class 'EntityB'."

I must have something wrong with my mappings, but I’m not sure what. Any ideas?

1
By the way, the error message above seems incorrect because "_AssociationList" in class "EntityB" is not a property. It's a field so it should not have a getter. This may be a problem with Fluent NHibernate. I'm currently using FNH version 1.1.0.685 with NHibernate version 2.1.2.4000.MylesRip
I tried switching to the latest version of FNH targeted for NH 2.1. This is version 1.2.0.712 of FNH. I now get a new, incorrect error message saying that some other "entity" doesn't have an Id mapped. The "entity" that it's complaining about is not an entity at all - it's an enum! Before I switched to this new version of FNH, I had exported the mappings. As far as I could tell, they looked correct to me. With the new version, no mappings are created at all so there's nothing to check. I am now convinced that I'm dealing with a bug in FNH.MylesRip
+1 for your thorough explanation with diagrams. Very helpful.Kurt Johnson

1 Answers

1
votes

I got it working now. So here's the trick... I reverted back to Fluent NHibernate version 1.1 (specifically 1.1.0.685). Then, although the mapping examples that use "Reveal.Member" don't show it as being necessary, I added "Access.Field()" to the mapping for both EntityA._AssociationList and EntityB._AssociationList. Here are the working mappings.

In the EntityA automapping override:

mapping.HasMany<EntityABLink>(Reveal.Member<EntityA>(“_AssociationList”)).Inverse().AsBag().Cascade.SaveUpdate().Access.Field(); 
mapping.IgnoreProperty(x => x.EntityBList);

In the EntityB automapping override:

mapping.HasMany<EntityABLink>(Reveal.Member<EntityB>(“_AssociationList”)).Inverse().AsBag().Cascade.SaveUpdate().Access.Field(); 
mapping.IgnoreProperty(x => x.EntityAList);

In the EntityABLink automapping override:

mapping.References<EntityA>(x => x.EntityA).Not.Nullable();
mapping.References<EntityB>(x => x.EntityB).Not.Nullable();

Once it was working in FNH 1.1, I tried upgrading to FNH 1.2. No good. I tried 1.2.0.694 as well as 1.2.0.712 and both of these still give the incorrect error message that a different "entity" (which is actually an enum!) doesn't have an Id mapped.

Fluent NHibernate is a wonderful tool so I hope that the bug in the latest version gets fixed. :-)