Remove from one side of the many to many in Nhibernate - nhibernate

I have these 2 objects in NHibernate forming a many to many relationship:
User:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Providers" namespace="Providers.Objects">
<class name="User" table="Users">
<id name="UserId" type="int">
<generator class="native" />
</id>
<many-to-one name="Application" column="ApplicationId" cascade="none" />
<property name="UserName" type="string" />
<property name="LoweredUserName" type="string" />
<property name="MobileAlias" type="string" />
<property name="IsAnonymous" type="bool" />
<property name="LastActivityDate" type="DateTime" />
<bag name="Roles" table="UsersInRoles" lazy="true" cascade="none" >
<key column="UserId"></key>
<many-to-many class="Role" column="RoleId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
And Role:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Providers" namespace="Providers.Objects">
<class name="Role" table="Roles">
<id name="RoleId" type="int">
<generator class="native" />
</id>
<many-to-one name="Application" column="ApplicationId" class="Application" cascade="none" />
<property name="RoleName" type="string" />
<property name="LoweredRoleName" type="string" />
<property name="Description" type="string" />
<bag name="Users" table="UsersInRoles" lazy="true" inverse="true" cascade="none" >
<key column="RoleId"></key>
<many-to-many class="User" column="UserId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
Let's say the role backupoperator has some users in it. If I try to remove one of the users from the role instance, like:
var backupoperator = GetRoleByName(session, app.ApplicationId, "backupoperator");
backupoperator.Users.RemoveAt(0);
session.Update(backupoperator);
transaction.Commit();
It doesn't work :( The association remains unchanged in the database. When I try the opposite (remove a role from a user object and updating the user object), it works.
Is it because of the inverse attribute in the NHibernate mapping?
How to accomplish what I am trying to do? (remove a user from a role, updating the role and having that persisted)?
Thanks

When you write inverse="true" you are telling NHibernate that the other side maintains the relationship.
Therefore, you have to remove the Role from the User's Roles collection if you want your change persisted.

Related

NHibernate map a column to a property and a bag

I'm having some difficulties mapping the following sitation:
I have a person with a connection id, I use this to get map a bag and a property.
This works if the database already exists. But for our unit tests, we generate one from the schema, it gives an error : "duplicate column names".
Here is the mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainLayer.SearchDomain" assembly="Test">
<class name="SearchPerson" table="person" lazy="false" mutable="false">
<id name="Id" column="`id`" type="int">
<generator class="identity" />
</id>
<property name="Name" column="`NAAM`" type="string" />
<property name="FirstName" column="`VOORNAAM`" type="string" />
<property name="ConnectionId" column="`Koppelid`" type="int" />
<bag name="Languages" lazy="false" mutable="false" access="field.camelcase-underscore">
<key property-ref="ConnectionId" column="Koppelid" />
<one-to-many class="DomainLayer.Person.LanguageSkill, Test" />
</bag>
</class>
</hibernate-mapping>
Problem: "Koppelid" both in Key property from the bag as in the property.
Edit:
The LanguageSkill mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainLayer.Person" assembly="Test">
<class name="LanguageSkill" table="languageskill" lazy="false">
<id name="Id" type="Int32">
<generator class="identity" />
</id>
<property name="ConnectionId" column="`KoppelId`" type="Int64" />
<property name="Remark" column="`Opmerking`" type="string" />
<property name="Source" column="`CdOorsprong`" type="string" />
<property name="MotherTongue" column="`moedertaal`" type="boolean" />
<property name="ModifiedDate" column="`wyzdat`" type="DateTime" />
</class>
</hibernate-mapping>
It should actually work. It may be because you have marked it with ` in one case, but not in the other.
Try this:
<bag ...>
<key property-ref="ConnectionId" column="`Koppelid`" />
the reason is actually that the column is defined twice in the LanguageSkillMapping. Since you mapped the column you could map it as reference and set the bag to inverse
<bag name="Languages" inverse="true" lazy="false" mutable="false" access="field.camelcase-underscore">
<key property-ref="ConnectionId" column="Koppelid" />
<one-to-many class="DomainLayer.Person.LanguageSkill, Test" />
</bag>
<many-to-one name="SearchPerson" column="`KoppelId`" />
Update: nevermind, Stefan got it

Joining tables in NHibernate gets "collection element mapping has wrong number of columns"

I'm having trouble mapping a many-to-many relationship with a join table. The errror message I am getting is "collection element mapping has wrong number of columns"
My db schema is basically:
.
Despite the picture from SQL Server Management Studio, the database is Sqlite.
My mapping file is:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Design" table="Design">
<id name="DesignID" column="DesignID" type="Int32">
<generator class="native"/>
</id>
<property name="Name" type="String" column="Name" />
<property name="DisplayOrder" type="Int32" column="DisplayOrder" />
<property name="ChangeDate" column="ChangeDate" type="DateTime"/>
<set name="DesignMeasures" table="DesignProperties" cascade="save-update,delete-orphan" >
<key column="DesignID" />
<many-to-many column="PropertyID" class="DesignProperties" unique="true" />
</set>
</class>
<class name="Properties" table="Properties">
<id name="PropertyID" column="PropertyID" type="Int32">
<generator class="native" />
</id>
<property name="Name" type="String" column="Name" />
<property name="Value" type="String" column="Value" />
</class>
<class name="DesignProperties" table="DesignProperties">
<composite-id>
<key-many-to-one class="Design" name="DesignID" column="DesignID" />
<key-many-to-one class="Properties" name="PropertyID" column="PropertyID" />
</composite-id>
</class>
</hibernate-mapping>
I know I'm doing something wrong, but I can't figure it out. What am I doing wrong? How do I fix it?
DesignProperties is either a many-to-many relationship or an entity, not both.
Since it doesn't have any properties of its own, I'd remove the DesignProperties class along with its mapping, and just leave a collection of Property (not Properties; your class name should be singular) in Design.

NHibernate AssertException: Interceptor.OnPrepareStatement(SqlString) returned null or empty SqlString

I am trying to switch a table from being a many-to-one mapping to being many-to-many with an intermediate mapping table. However, when I switched it over and tried to do a query on it with NHibernate, it's giving me this error: "Interceptor.OnPrepareStatement(SqlString) returned null or empty SqlString."
My query was originally something more complex, but I switched it to a basic fetch all and I'm still having the problem:
Session.QueryOver<T>().Future();
It would seem to either be a problem in my model mapping files or something in my database.
Here are my model mappings:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="GBI.Core" namespace="GBI.Core.Models">
<class name="Market" table="gbi_Market">
<id name="Id" column="MarketId">
<generator class="identity" />
</id>
<property name="Name" />
<property name="Url" />
<property name="Description" type="StringClob" />
<property name="Rating" />
<property name="RatingComment" />
<property name="RatingCommentedOn" />
<many-to-one name="RatingCommentedBy" column="RatingCommentedBy" lazy="proxy"></many-to-one>
<property name="ImageFilename" />
<property name="CreatedOn" />
<property name="ModifiedOn" />
<property name="IsDeleted" />
<many-to-one name="CreatedBy" column="CreatedBy" lazy="proxy"></many-to-one>
<many-to-one name="ModifiedBy" column="ModifiedBy" lazy="proxy"></many-to-one>
<set name="Content" where="IsDeleted=0 and ParentContentId is NULL" order-by="Ordering asc, CreatedOn asc, Name asc" lazy="extra">
<key column="MarketId" />
<one-to-many class="MarketContent" />
</set>
<set name="FastFacts" where="IsDeleted=0" order-by="Ordering asc, CreatedOn asc, Name asc" lazy="extra">
<key column="MarketId" />
<one-to-many class="MarketFastFact" />
</set>
<set name="NewsItems" table="gbi_NewsItem_Market_Map" lazy="true">
<key column="MarketId" />
<many-to-many class="NewsItem" fetch="join" column="NewsItemId" where="IsDeleted=0"/>
</set>
<!--<set name="MarketUpdates" table="gbi_Market_MarketUpdate_Map" lazy="extra">
<key column="MarketId" />
<many-to-many class="MarketUpdate" fetch="join" column="MarketUpdateId" where="IsDeleted=0" order-by="CreatedOn desc" />
</set>-->
<set name="Documents" table="gbi_Market_Document_Map" lazy="true">
<key column="MarketId" />
<many-to-many class="Document" fetch="join" column="DocumentId" where="IsDeleted=0"/>
</set>
</class>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="GBI.Core" namespace="GBI.Core.Models">
<class name="MarketUpdate" table="gbi_MarketUpdate">
<id name="Id" column="MarketUpdateId">
<generator class="identity" />
</id>
<property name="Description" />
<property name="CreatedOn" />
<property name="ModifiedOn" />
<property name="IsDeleted" />
<!--<many-to-one name="Market" column="MarketId" lazy="proxy"></many-to-one>-->
<set name="Comments" where="IsDeleted=0" order-by="CreatedOn desc" lazy="extra">
<key column="MarketUpdateId" />
<one-to-many class="MarketUpdateComment" />
</set>
<many-to-one name="CreatedBy" column="CreatedBy" lazy="proxy"></many-to-one>
<many-to-one name="ModifiedBy" column="ModifiedBy" lazy="proxy"></many-to-one>
</class>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="GBI.Core" namespace="GBI.Core.Models">
<class name="MarketUpdateMarketMap" table="gbi_Market_MarketUpdate_Map">
<id name="Id" column="MarketUpdateMarketMapId">
<generator class="identity" />
</id>
<property name="CreatedOn" />
<property name="ModifiedOn" />
<property name="IsDeleted" />
<many-to-one name="CreatedBy" column="CreatedBy" lazy="proxy"></many-to-one>
<many-to-one name="ModifiedBy" column="ModifiedBy" lazy="proxy"></many-to-one>
<many-to-one name="MarketUpdate" column="MarketUpdateId" lazy="proxy"></many-to-one>
<many-to-one name="Market" column="MarketId" lazy="proxy"></many-to-one>
</class>
As I mentioned, MarketUpdate was originally a many-to-one with Market (MarketId column is still in there, but I'm ignoring it. Could this be a problem?). But I've added in the Market_MarketUpdate_Map table to make it a many-to-many.
I'm running in circles trying to figure out what this could be. I couldn't find any reference to this error when searching. And it doesn't provide much detail.
Using:
NHibernate 2.2
.NET 4.0
SQL Server 2005
Turns out the problem was just that the xml mapping file was set as content instead of embedded resource in visual studio. Changing that fixed all my problems.
If you're using Fluent rather than xml for mappings and encounter this issue, try deleting the generated xml files in %root%\LocalCache\NHibernate.

NHibernate Mapping + Iset

I have a mapping file
<set name="Friends" table="Friends">
<key column="UserId"/>
<many-to-many class="User" column="FriendId"/>
</set>
I would like to specify extra columns for the friend table this creates.
For example Approve (the user must approve the friend request)
Is there a easy way?
And update
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="MyVerse.Domain" assembly="MyVerse.Domain" xmlns="urn:nhibernate-mapping-2.2">
<class name="User" table="[User]" lazy="true">
<id name="Id" type="Guid">
<generator class="guid" />
</id>
<property name="DateCreated" type="DateTime" not-null="true" />
<property name="Deleted" type="Boolean" not-null="true" />
<property name="Firstname" type="String" length="100" not-null="true" />
<property name="Lastname" type="String" length="100" not-null="true" />
<bag name="Friends" table="[Friend]">
<key column="UserId"/>
<many-to-many class="Friend" column="FriendId"/>
</bag>
</class>
<class name="Friend" table="[Friend]" lazy="true">
<id name="Id" type="Guid">
<generator class="guid" />
</id>
<property name="DateCreated" type="DateTime" not-null="true" />
<property name="Approved" type="Boolean" not-null="true" />
</class>
</hibernate-mapping>
Will cause a link to the friend table from the friend table
If a set has "extra properties", you must convert it into a proper entity.
So, a User doesn't have an ISet<User>; it has an ISet<Friend>.

NHibernate Many-to-many

I have a legacy database and I am trying to create a NHibernate DAL.
I have a problem with a mapping on Many-To-Many table.
The Database tables:
studio_Subscribers
studio_Groups (contains a IList of Subscribers)
studio_Subscribers_Groups - Many-To-Many table with primary keys
The problem is when I create a SubscriberGroup instance and fill it with Subscribers they gets saved to the studio_Subscribers table but not to the Many-To-Many table.
I cant figure out whats wrong?
studio_Subscribers table mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Meridix.Studio.Common"
namespace="Meridix.Studio.Common">
<class name="SubscriberItem" table="studio_Subscribers">
<id name="StorageId" column="Id" unsaved-value="0" access="nosetter.camelcase">
<generator class="identity" />
</id>
<property name="Id" column="DomainId" not-null="true" />
<property name="Subscriber" column="Subscriber" not-null="true" length="50" />
<property name="Description" column="Description" not-null="false" length="100" />
<property name="Type" column="Type" not-null="true" length="40"
type="Meridix.Studio.Data.Repositories.EnumStringTypes.SubscriberTypeEst, Meridix.Studio.Data.Repositories" />
</class>
</hibernate-mapping>
studio_Groups table mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Meridix.Studio.Common"
namespace="Meridix.Studio.Common">
<class name="SubscriberGroup" table="studio_Groups">
<id name="StorageId" column="Id" unsaved-value="0" access="nosetter.camelcase">
<generator class="identity" />
</id>
<property name="Id" column="DomainId" not-null="true" />
<property name="Name" column="Name" not-null="true" length="200" />
<property name="Description" column="Description" not-null="false" length="300" />
<bag name="Subscribers" table="studio_Groups_Subscribers" access="nosetter.camelcase">
<key column="GroupId"></key>
<many-to-many column="SubscriberId" class="SubscriberItem" />
</bag>
</class>
</hibernate-mapping>
Shouldn't you have an appropriate bag on your subscriber aswell with a many-to-many relation to the group?
<bag name="Groups" table="studio_Groups_Subscribers" access="nosetter.camelcase">
<key column="SubscriberId"></key>
<many-to-many column="GroupId" class="GroupItem" />
</bag>
and maybe have a cascade="save-update" on your SubscriberItems collection so that saving the Group will update your children with the appropriate relation from "the other side".
I believe it is to do with the cascade options, check out Ayende's post on it and I imagine with a bit of Googling you can find the correct syntax to go into your hbm file. I use fluent-NHibernate so I can't help you with the XML file.
http://ayende.com/Blog/archive/2006/12/02/NHibernateCascadesTheDifferentBetweenAllAlldeleteorphansAndSaveupdate.aspx