Having trouble mapping HBM to FluentNHibernate mappings for many-to-many
TL; DR: I'm trying to map a many-to-many with an additionaly Order
column on the many-to-many table. HBM works great. Can't get FluentNHibernate mappings to work.
I have the following two classes which I'm trying to map with a many-to-many relationship:
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
private IDictionary<int, Profile> profilesMap;
public virtual IEnumerable<Profile> Profiles { get { return this.profilesMap.Select(kvp => kvp.Value); } }
public User()
{
this.profilesMap = new SortedDictionary<int, Profile>();
}
public virtual void Add(Profile x)
{
profilesMap.Add(x);
}
}
public class Profile
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
The key of the profilesMap
is the profile order. Therefore, the HBM mappings are as follows:
<class name="User" table="User">
<id name="Id" column="Id" type="integer">
<generator class="native" />
</id>
<property开发者_JAVA百科 name="Name" type="string" column="Name" />
<map name="profilesMap" access="field.camelcase" table="User_Profile">
<key column="User_Id" />
<index column="`Order`" type="integer" />
<many-to-many class="Profile" column="Profile_Id" />
</map>
</class>
<class name="Profile" table="Profile">
<id name="Id" column="Id" type="integer">
<generator class="native" />
</id>
<property name="Name" type="string" column="Name" />
</class>
This works perfectly and creates the correct many-to-many table:
create table User_Profile (
User_Id INT not null,
Profile_Id INT not null,
"Order" INT not null,
primary key (User_Id, "Order"),
constraint FK6BDEDC07D1EDE651 foreign key (Profile_Id) references Profile,
constraint FK6BDEDC07650CB01 foreign key (User_Id) references User
)
However, I don't particularly like using HBM because it's not really refactor-friendly. Therefore, I'm trying to translate this to FluentNHibernate. This is my attempt at the user's many-to-many mapping:
public class UserMap : ClassMap<User>
{
public UserMap()
{
this.Id();
this.Map(o => o.Name);
var mapMember = Reveal.Member<User, IEnumerable<KeyValuePair<int, Profile>>>("profilesMap");
this.HasManyToMany<KeyValuePair<int, Profile>>(mapMember)
.Access.CamelCaseField()
.AsMap<int>("`Order`")
.Table("User_Profile");
}
}
I expected this to work, however it blows up when trying to build the session factory:
An association from the table User_Profile refers to an unmapped class: System.Collections.Generic.KeyValuePair`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[ConsoleApplication9.Profile, ConsoleApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
I did quite a lot of research on how to get AsMap
to work and so I changed it to the following:
this.HasManyToMany<KeyValuePair<int, Profile>>(mapMember)
.Access.CamelCaseField().AsMap<int>("`Order`")
.Element("Profile_id", ep => ep.Type<int>()) // Added.
.Table("User_Profile");
However, this produces an incorrect table by not including the foreign key (or not null constraint) from Profile_id
:
create table User_Profile (
User_id INT not null,
Profile_id INT,
"Order" INT not null,
primary key (User_id, "Order"),
constraint FK6BDEDC07650CB01 foreign key (User_id) references "User")
Additionally, it also blows up when trying to add a profile to a user:
var a = new User() { Name = "A" };
var b = new Profile() { Name = "B" };
a.Add(b);
session.Save(b);
session.Save(a);
session.Flush(); // Error: Unable to cast object of type 'ConsoleApplication9.Profile' to type 'System.IConvertible'.
So - I've been pulling my hair out for hours trying to determine how to properly map this relationship with FNH, but I just can't seem to get it. Simple many-to-many relationships seem easy, but when trying to add the index column, it doesn't seem to work. I would appreciate it if anyone is will to help me solve this problem.
Have you seen this answer to a similar question?
How to map IDictionary<string, Entity> in Fluent NHibernate
One problem I see is the use of the Element in your manytomany. That's probably why you don't get any foreign key setup for profile_id. Also, if possible, I'd try adding a public accessor for profilesMap.
To match your case, I'd make your mapping as follows:
HasManyToMany<Profile>(ProfilesMap) //Assuming the addition of a public get accessor
.Access.CamelCaseField()
.ParentKeyColumn("User_Id")
.ChildKeyColumn("Profile_Id")
.Table("User_Profile")
.AsMap<int>("Id");
If you can't add the accessor you can try this
HasManyToMany<Profile>(Reveal.Member<User, object>("profilesMap"))
.Table("User_Profile")
.ParentKeyColumn("User_id")
.ChildKeyColumn("Profile_id")
.AsMap<int>("`Order`");
精彩评论