I have many to many relationship in my application and I am using fluent nhibernate.
A login can have many roles. ( A role can also have many logins ).
I have seen many examples of using a composite primary key.
HasManyToMany<Role>(x => x.Roles).Table("Role")
.ParentKeyColumn("RoleId")
.ChildKeyColumn("LoginId");
Do you know if Fluent/NHibernate supports Many to Many relationships without having the database associative entity (Login_Role) requiring a composite primary key.
I would prefer to have Login_Role use a surrogate primary key.
Cheers,
Andrew
You can use an idbag:
<idbag name="Roles" table="Login_Role">
<collection-id type="int" column="id">
<generator class="hilo"/>
</collection-id>
<key column="LoginId"/>
<many-to-many class="Role" column="RoleId"/>
</idbag>
I don't think Fluent exposes it yet.
Related
Following on from NHibernate one-to-one vs 2 many-to-one
Is there an easy way to maintain multiple one-to-many relationships which are being used as a pseudo one-to-one.
E.g.
If I have 2 entities, User and Contact, which are related by a FK on each (User.ContactId and Contact.UserID).
What is the best way to maintain that each reference points at the other. It would be wrong for the system to update User with a different contact, but the Contact still references User...
Most likely you don't need to maintain this at all if you remove one of redundant foreign keys. Your database schema should not allow anomalies like that (userX references contactX but contactX references userY). Conceptually you have one-to-one relationship between user and contact. Why not have one-to-one in NHibernate mappings? If this is because of lazy loading that is not supported for nullable one-to-one in NHibernate? There is a solution to this problem that does not involve redundant foreign keys in the database.
1) In User mapping define a bogus list. List can have only one or zero items. Zero is treated as NULL (no Contact).
<bag
name="_contact"
table="UserContacts"
lazy="true"
inverse="true"
cascade="all-delete-orphan" >
<key column="UserId" />
<one-to-many class="Contact" />
</bag>
In Contact mapping define one-to-one:
<one-to-one name="_user" class="User" constrained="true" />
In the database you need to have PK Users.Id and one (!) foreign key Contacts.UserID.
2) Another option is to simply have many-to-one in User mapping and one FK Users.ContactId
<many-to-one
name="_contact"
column="ContactId"
cascade="all-delete-orphan"
unique="true"
lazy="proxy"/>
Either way the maintenance that you asked about is not needed and anomalies are not possible.
We have a fairly big DB (~200 tables) which almost entirely uses composite primary keys and composite foreign keys, using a single "base table" from which every other table inherits part of its primary key:
Parent has single column primary key ParentId
Child has composite primary key (ParentId, ChildId) and foreign key ParentId
Nephew has composite primary key (ParentId, NephewId), foreign key ParentId and foreign key (ParentId, ChildId)
and so on. Up until now we managed this whole shebang with an ORM framework of our own, but we're considering using NHibernate, which I've been assigned to learn (i've downloaded v2.1.2).
The mappings:
Child
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Assembly" namespace="Namespace">
<class name="Child" table="Child">
<composite-id name="IdChild" class="ChildId">
<key-many-to-one name="Parent" column="ParentId" class="ParentId"></key-many-to-one>
<key-property name="Id" column="ChildId" type="Int32"></key-property>
</composite-id>
<!--simple properties-->
<set name="Nephews" table="Nephew">
<key>
<column name="ParentId"></column>
<column name="ChildId"></column>
</key>
<one-to-many class="Nephew"/>
</set>
</class>
</hibernate-mapping>
Nephew
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Assembly" namespace="Namespace">
<class name="Nephew" table="Nephew">
<composite-id name="IdNephew" class="NephewId">
<key-many-to-one name="Parent" column="ParentId" class="Parent"></key-many-to-one>
<key-property name="Id" column="NephewId" type="Int32"></key-property>
</composite-id>
<many-to-one name="Child" class="Child">
<column name="ParentId"></column>
<column name="ChildId"></column>
</many-to-one>
<!--simple properties-->
</class>
I can post the classes too if you want, I'll omit them now for brevity (as I'll omit the mapping for Parent, since it doesn't have problems). Every property is virtual, every mapping file is an embedded resource, every composite Id has its own class which overrides Equals and GetHashCode.
The problem is I can't save an instance of Nephew, initialized through a simple new Nephew() and passed on to _session.Save(), because I get a System.IndexOutOfRangeException: Invalid index n for this SqlParameterCollection with Count=n..
The only column which is duplicated in the mapping is the ParentId. Removing the many-to-one mapping in the Nephew, the set mapping in the Child and all related properties everything works fine.
I found several posts reporting this exception, and the most appropriate in my case seems to be this one, which gives me the gut feeling that my current schema is infeasible with NHibernate. Please tell me I'm wrong :-)
NOTES:
I'm not using Fluent right now, even though it may be an option, but I preferred learning the basics first;
Yes, we realize composite primary keys are a big pain in the ass. This DB has passed through several hands through the years, probably not so skilled ones, but before refactoring it we will count to 10 000
Unfortunately, you won't be able to map that relationship in its current form, as you have inferred.
However, there's a workaround. Instead of mapping Nephew.Child as a many-to-one, map ChildId as a regular property, and use a query when you need to retrieve the Child.
There's one more chance for a hack. If and only if Nephew.Child is not null and not mutable, you could map the key referencing the Child instead of the parent:
<class name="Nephew" table="Nephew">
<composite-id name="IdNephew" class="NephewId">
<key-many-to-one name="Child">
<column="ParentId">
<column="ChildId">
</key-many-to-one>
<key-property name="Id" column="NephewId"/>
</composite-id>
</class>
I found a better solution:
<many-to-one name="Child" class="Child">
<formula>ParentId</formula>
<column name="ChildId"></column>
</many-to-one>
I had already tried that and it gave me an error, but then I noticed this patch, so I downloaded the 3.0.0 alpha2 and it all worked correctly! Thanks to this solution I can map the Nephew.Child property as it was meant to be.
I still need the Child.Add(Nephew) method, though (but I realized that is recommended even in the documentation)
I have two entities, C and P.
C is mapped to P in a one-to-one association, with lazy="no-proxy",
like this: (P's mapping:)
<one-to-one name="c" class="C" property-ref="P" access="field" lazy="no-proxy"/>
P is mapped to C in a many-to-one association, like this: (C's mapping:)
<many-to-one name="p" column="PId" class="P" access="field" lazy="no-proxy" not-null="false"/>
usually I use lazy fetching, but in some cases I use FetchMode.Join to avoid the N+1 SELECTs problem, like this:
criteria.SetFetchMode("p", FetchMode.Join)
however, using FetchMode.Join for the C entity performs the query with a left outer join, and then immediately performs N more queries, fetching P's by ID! (profiling courtesy of NHProf)
any idea why this is happening?
answering #KLE's question, here's an excerpt from the documentation here
For a primary key association, add
the following mappings to Employee and
Person respectively:
<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>
Ensure that the primary keys of the related rows in the PERSON and EMPLOYEE tables are equal.
Alternatively, a foreign key with a
unique constraint, from Employee to
Person, can be expressed as:
<many-to-one name="person" class="Person" column="PERSON_ID" > unique="true"/>
This association can be made
bidirectional by adding the following
to the Person mapping:
<one-to-one name="employee" class="Employee" property-ref="person"/>
please let me know if I misunderstood this. thanks.
Don't know which version of NHibernate you are using but with 2.1.0 - 2.1.2 i've had some issues regarding FetchModes and complex mappings. I hadn't ran into property-ref (which obviously confuses the property walker) but i did have problems with some union classes.
Have you tried setting lazy="true" / "extra" and then define FetchMode.Join ?
I am trying to get a collection of objects into a parent object through mapping.
I have a parent object "ScoreCard" whose primary key is a guid (Id) and a child "Score" object whose primary key is a guid (Id). I want to select the child objects for the parent based on two fields that both objects have but I can't get it to work, here's the mapping
<bag name="ScoreCard">
<key>
<column name="HoleId"/>
<column name="PlayerId"/>
</key>
<one-to-many class="Score" not-found="ignore"/>
</bag>
I didn't design the database but the ScoreCard object comes from a view that returns both column I need plus the evil guid. Whatever I've tried, NHibernate keeps throwing an exception about the foreign key not being the same as the primary key.
This seems to me to be the most simple requirement, get a collection of things given some criteria, why am I so stuck?
Thanks for your help, sorry for the bad example code (subliminal golf watching at relatives house).
Well, I found it eventually. The parent object is drawn from a view giving three columns and no key. I can map a composite key to the HoleId and PlayerId instead of the evil guid that I found when I looked at the code. This is great as I can easily map the Score objects I need and then lazy load them using NHibernateUtil.Initialize.
My mapping xml needs to look like this
<class name="ParentObject">
<composite-id>
<key-property name="HoleId" column="HoleId" />
<key-property name="PlayerId" column="PlayerId" />
</composite-id>
<property name="EvilGuid" column="Id" />
<bag name="ScoreCard">
<key>
<column name="HoleId"/>
<column name="PlayerId"/>
</key>
<one-to-many class="Score" not-found="ignore"/>
</bag>
</class>
I got my inspiration from this post, please also pay attention to Stefan's answer as I feel I had a lucky break here, and the design could be made better with more thought about DDD.
Thanks for your help.
The problem is this: NHibernate works best (but not only) for DDD, this means for creating domain classes first and make the database best fitting the domain model.
You have a composite-id relation to non-primary-key fields. So start praying that NHibernate can cope with that. Both composite-ids and relations by non-primary-keys are supported - for legacy databases - and generally discouraged for DDD.
I think the combination of both does not work. See this issue on NHibernates issue tracker:
https://nhibernate.jira.com/browse/NH-1722. You can vote for the feature there.
I have a NewsFeed object mapped as such:
<class name="NewsFeed">
<id name="NewsFeedId">
<generator class="guid"/>
</id>
<property name="FeedName" not-null="true" />
<property name="FeedURL" not-null="true" />
<property name="FeedIsPublished" not-null="true" />
</class>
And Users who can have a Set of Selected feeds that they might be intereseted in, mapped like so:
<class name="SystemUser">
<id name="SystemUserId">
<generator class="guid"/>
</id>
<set name="SelectedNewsFeeds" table="SystemUserSelectedNewsFeeds" cascade="all">
<key column="SystemUserId" />
<many-to-many column="NewsFeedId" class="NewsFeeds.NewsFeed, Domain"/>
</set>
</class>
What I want to happen is when I delete the parent NewsFeed then all of the SelectedNewsFeed references get deleted too, without having to load each SystemUser and delete the NewsFeed by hand.
What is the best way to achieve this?
UPDATE: Using cascade="all-delete-orphan" instead of "all" still results in an exception when deleting the NewsFeed:
The DELETE statement conflicted with the REFERENCE constraint "FKC8B9DF81601F04F4". The conflict occurred in database "System", table "dbo.SystemUserSelectedNewsFeeds", column 'NewsFeedId'.
JMCD
Your second approach:
Another alternative is to break the
many-to-many relationship with a join
class in the middle which nHiberate
would be able to determine
parent-child relationships and the
cascade should work.
is actually what the nHibernate folks recommend in their documentation.
Don't use exotic association mappings.
Good usecases for a real many-to-many
associations are rare. Most of the
time you need additional information
stored in the "link table". In this
case, it is much better to use two
one-to-many associations to an
intermediate link class. In fact, we
think that most associations are
one-to-many and many-to-one, you
should be careful when using any other
association style and ask yourself if
it is really neccessary.
Using two one-to-many associations adds the flexibility to easily add other attributes to the "subscription", such as notification preferences for that particular subscription.
Since the relation inside the set is many-to-many, nHibernate is not able to tell which end of the relationship is the child and which is the parent, and the quickest way for me to achieve what I wanted was just to write some SQL that I sent through my repository that deleted the respective news feeds from the collection, and then deleted the parent news feed. The next time the collection was hydrated the changes were reflected.
Another alternative is to break the many-to-many relationship with a join class in the middle which nHiberate would be able to determine parent-child relationships and the cascade should work.
change
cascade="all"
to
cascade="all-delete-orphan"
Reference