Many-to-many mapping of Joined Subclasses

20 views
Skip to first unread message

Chris Fazeli

unread,
Jul 30, 2009, 3:53:30 PM7/30/09
to Fluent NHibernate
Hi,

I have a Customer entity and a Manager entity. Both are joined
subclasses of a Business entity.
One customer can be managed by many managers:
public virtual IList<Manager> Managers { get; set; }
One manager can manage many customers:
public virtual IList<Customer> Customers { get; set; }

It is my understanding that automapping doesn't pick this up. So I map
manually with overrides:
public class CustomerMap : IAutoMappingOverride<Customer>
{
public void Override(AutoMap<Customer> mapping)
{
mapping.HasManyToMany<Manager>(x => x.Managers)
.AsBag()
.WithTableName("ManagersCustomers")
.WithParentKeyColumn("CustomerFk")
.WithChildKeyColumn("ManagerFk");
}
}

public class ManagerMap : IAutoMappingOverride<Manager>
{
public void Override(AutoMap<Manager> mapping)
{
mapping.HasManyToMany<Customer>(x => x.Customers)
.AsBag()
.WithTableName("ManagersCustomers")
.WithParentKeyColumn("ManagerFk")
.WithChildKeyColumn("CustomerFk")
.Inverse();
}
}

These overrides are executed, but in
FluentNhibernate.AutoMap.AutoPersistenceModel, the MergeMap method
calls GetTypeToMap, and this does a check to see if the type's
basetype should be passed back. I get a failure here. Using S#arp
Arch's CanConfirmDatabaseMatchesMappings test, I see the exeption:

----> System.ArgumentException : Object of type
'FluentNHibernate.AutoMap.AutoMap`1[Project.Customer]' cannot be
converted to type 'FluentNHibernate.AutoMap.AutoMap`1
[Project.Business]'.
TearDown : System.Reflection.TargetInvocationException : Exception has
been thrown by the target of an invocation.
----> System.Collections.Generic.KeyNotFoundException : The given
key was not present in the dictionary.


Can anyone help with this? I'm not quite sure what to do about it!

For completeness, there's also a one-to-many mapping between Managers
and Customers, where the customer has the notion of a PrimaryManager,
and an inverse relationship exists in the Manager as a list of
ManagedCustomers. I figure this gets picked up by the automapper.

Thanks,
- Chris

Chris Fazeli

unread,
Jul 30, 2009, 6:07:22 PM7/30/09
to Fluent NHibernate
So I've tried this mapping using and Override for the Business entity,
rather than its joined subclasses:

public class BusinessMap : IAutoMappingOverride<Business>
{
public void Override(AutoMap<Business> mapping)
{
mapping.JoinedSubClass<Manager>("BusinessId")
.HasManyToMany<Customer>(x => x.SecondaryCustomers)
.WithTableName("ManagersToCustomers")
.WithParentKeyColumn("ManagerFk")
.WithChildKeyColumn("CustomerFk");

mapping.JoinedSubClass<Manager>("BusinessId")
.HasMany<Customer>(x => x.ManagedCustomers)
.KeyColumnNames.Add("PrimaryManagerFk")
.Inverse();

mapping.JoinedSubClass<Customer>("BusinessId")
.HasManyToMany<Manager>(x => x.SecondaryManagers)
.WithTableName("ManagersToCustomers")
.WithParentKeyColumn("CustomerFk")
.WithChildKeyColumn("ManagerFk");

mapping.JoinedSubClass<Customer>("BusinessId")
.References<Manager>(x => x.PrimaryManager);
}
}

It seems odd to have to do it this way, but if you explicitly state
the inverse and the link table name, the mapping is done correctly:
<joined-subclass name="Project.Manager, Project, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null" table="Managers">
<key column="BusinessId" />
...
<bag name="SecondaryCustomers" table="ManagersToCustomers">
<key column="ManagerFk" />
<many-to-many column="CustomerFk" class="Project.Customer,
Project, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>
</joined-subclass>
<joined-subclass name="Project.Customer, Project, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null" table="Customers">
<key column="BusinessId" />
...
<bag name="SecondaryManagers" table="ManagersToCustomers">
<key column="CustomerFk" />
<many-to-many column="ManagerFk" class="Project.Manager, Project,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>
</joined-subclass>

However, the Inverse HasMany mapping does not work, giving me a
completely incorrect result under the Manager subclass:
<bag name="ManagedCustomers" inverse="true">
<key column="ManagerFk" />
<one-to-many class="Project.Customer, Project, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null" />
</bag>

I'd expect the key column to be "PrimaryManagerFk" (as it was passed
into the mapping), and the one-to-many class to be "Project.Manager",
since one Manager has many Customers (this bag).

Any ideas what's going on? Why is the mapper not using my passed in
KeyColumnName. What automatic detection should I be able to rely on?

- Chris

Chris Fazeli

unread,
Jul 31, 2009, 1:13:34 PM7/31/09
to Fluent NHibernate
Regarding this statement:
I'd expect the key column to be "PrimaryManagerFk" (as it was passed
into the mapping), and the one-to-many class to be "Project.Manager",
since one Manager has many Customers (this bag).

I think I was wrong about the second point, and the one-to-many class
is correctly set to Project.Customer. However, for the life of me I
cannot get the KeyColumnName to be set correctly.

A hypothesis that I have is that the automapping combined with the
override is confusing FluentNH because there is both a "many-to-one"
and a "many-to-many" relationship between the same two tables:

1 Manager -> many Customers (as Primary Manager)
many Managers -> many Customers (as Secondary Managers)

I've tried stepping through the Fluent mapping code, but I'm unsure
where exactly the problem occurs. I can't find where the KeyColumnName
is supposed to get used to generate the correct mapping...
Reply all
Reply to author
Forward
0 new messages