Nhibernate One To Many With Criteria - c#

I've got a class which contains a list of associated objects.
I've map it as follows:
<class name="MyDto" table="`MyView`" mutable="false" lazy="false">
<id name="Id" column="ID" type="int">
<generator class="assigned" />
</id>
<set name="Days" cascade="all-delete-orphan" lazy="false" >
<key column="Id" />
<one-to-many class="MyAssociatedObj"/>
</set>
</class>
<class name="MyAssociatedObj" table="V_SC_NEED" mutable="false" lazy="false" >
<id name="Id" column="`ID2`" type="int">
<generator class="assigned" />
</id>
<property type="DateTime" not-null="true" name="DayDate" column="`Date`" />
<component name="Audit">
<property type="string" not-null="true" length="255" name="Username" column="`USERNAME`" />
<property type="decimal" not-null="true" name="PreviousValue" column="`PrevVal`" />
</component>
</class>
What I wanted to do is to get a list of MyDto Object, filtering by my associated object, MyAssociatedObject.
Which means, for example, if i do:
UnitOfWork.Session.CreateCriteria<MyDto>().List<MyDto>()
I get a list of 42 lines.
Now, I want to filter them by my associated object, by a restriction of between.
the problem is that when i do so:
UnitOfWork.Session.CreateCriteria<MyDto>().CreateAlias("Days", "days").Add(Restrictions.Between("days.DayDate", query.FromDate, query.ToDate));
I'm getting, somehow 684 rows ! (some cartesial product or something).
Any advise?
Many thanks !

You are getting more rows in the second query because you are JOINing with MyAssociatedObj, which seems to be in a many-to-one relationship with your MyDTO class.
If you want to filter out duplicates of MyDTO, you can use the DistinctRootResultTransformer:
UnitOfWork.Session.CreateCriteria<MyDto>().CreateAlias("Days", "days")
.Add(Restrictions.Between("days.DayDate", query.FromDate, query.ToDate))
.SetResultTransformer(
new NHibernate.Transform.DistinctRootEntityResultTransformer());
Hope it helps.

Related

How to make a Many-to-One relationship with a link table in between?

I'm having problems getting the right hbm.xml for mapping a Many-to-One relationship over a link table:
<class name="Car" table="Cars" lazy="true">
<id name="CarKey" type="int">
<generator class="native" />
</id>
[properties]...
<many-to-one ??? />
</class>
<class name="Driver" table="Drivers" lazy="true">
<id name="DriverKey" type="int">
<generator class="native" />
</id>
[properties]...
</class>
<class name="CarDriverLink" table="CarDriverLinks" lazy="true">
<id name="CarDriverLinkKey" type="int">
<generator class="native" />
</id>
<property name="CarKey">
<column name="CarKey" sql-type="int" not-null="true" />
</property>
<property name="DriverKey">
<column name="DriverKey" sql-type="int" not-null="true" />
</property>
</class>
Imagining that in this example a car can have only one driver, but a driver can have multiple cars, how would I add a many-to-one relationship onto the Car mapping to allow a Car to see which Driver can drive it, using the CarDriverLinks table?
So for a one-to-many, many-to-one you don't necessarily need a cross reference table in between. That will give you a many-to-many relationship. Since each car will have only one driver, you can add a DriverID to your Car table and class. Your driver can still have multiple cars. I use Fluent NHibernate, so I don't really remember the XML mapping but check out this question that explains the rest of what you're looking for. One To Many, Many To One - NHibernate

Mapping a non existing column in Nhibernate

I am working with an Oracle legacy database and I am trying to map a relationship with two tables. Unfortunately the company who designed the database used composite keys.
I've got this table (ORDERS) which has a primary key composed on 3 fields: ordernumber (number), version and company.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BpSalesOrders.Domain"
namespace="BpSalesOrders.Domain">
<class name="Order" table="OCSAORH" mutable="false" where="OCHAMND = 0">
<cache usage="read-only"/>
<composite-id>
<key-property name="Number" column="OCHORDN" type="String" length="10"></key-property>
<key-property name="Ver" column="OCHAMND" type="Int32"></key-property>
<key-property name="Company" column="OCHCOSC" type="String" length="5"></key-property>
</composite-id>
<property name="Reference" column="OCHOCNO" type="String" length="25"></property>
<property name="Date" column="OCHOCDT" type="Int32"></property>
<property name="Status" column="OCHSTA1" type="Char"></property>
<property name="LineOfCreditStatus" column="OCHSTA4" type="Char"></property>
<property name="WareHouse" column="OCHRESP" type="String"></property>
<set name="OrderLines" access="field.pascalcase-underscore" inverse="true" lazy="true" mutable="false">
<key>
<column name="OCLORDN" not-null="true"/>
<column name="OCLAMND" not-null="true" default="0"/>
<column name="OCLCOSC" not-null="true"/>
</key>
<one-to-many class="OrderLine" not-found ="ignore"/>
</set>
<set name="OrderReleases" access="field.pascalcase-underscore" inverse="true" lazy="true" mutable="false">
<key>
<column name="ORLORDINE" not-null="true" />
<column name="ORLSOCIETA" not-null="true"/>
</key>
<one-to-many class="OrderRelease" not-found ="ignore"/>
</set>
</class>
</hibernate-mapping>
There's a relation with OrderLines which works fine cause that table has the same exact key structure.
I am having problem with the OrderReleases relation. That table has got a primary key composed on a OrderNumber, Company and date. There's no version here:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BpSalesOrders.Domain"
namespace="BpSalesOrders.Domain">
<class name="OrderRelease" table="OCBPORDRIL" mutable="true">
<composite-id>
<key-property name="Company" column="ORLSOCIETA" type ="String" length="5"></key-property>
<key-property name="OrderNumber" column="ORLORDINE" type ="String" length="10"></key-property>
<key-property name="RequestDate" column="ORLDATARICHIESTA" type ="Int32"></key-property>
</composite-id>
<property name="Status" column="ORLSTATUS" type="Char" length="1"></property>
<property name="StatusMessage" column="ORLSEGNALAZIONE" type="String" length="90"></property>
<property name="ProcessingDate" column="ORLDATAELABORA" type ="Int32"></property>
</class>
</hibernate-mapping>
Considering that the relationship is one-to-many I would like to join the table Orders and OrderRelease on OrderNumber and Company. If I try to do that (as in the example) nHibernate throws an exception telling me that the key it's trying to map is composed with 3 fields.
Is there a way to create an non-existing column which wouldn't be persisted so that I can setup my mapping and relations?
Any help will be apreciated.
Ended up creating a view with a fake column.

NHibernate column uniqueness and inheritance

Consider the mapping file below. Both classes have a different notion of what the businessId is, which the object model handles, but it is acceptable to store it as a plain string in one column. I also want them each to have the same relationship with the Allocations set, which is the main reason I want them mapped using inheritance.
While the odds of a business id for a Project being the same as an Account are remote today, it would be ideal if there were a way to make the combination of the businessId and discriminator both part of the natural-id. This is not allowed, maybe for some good reason that I am not seeing.
Can I improve this mapping in some obvious way?
Cheers,
Berryl
<class name="ActivitySubject" table="ActivitySubjects" discriminator-value="BASE_CLASS">
<id name="Id" unsaved-value="0">
<column name="ActivitySubjectId" />
<generator class="hilo" />
</id>
<discriminator column="ActivitySubjectType" type="System.String" />
<natural-id mutable="true">
<property name="BusinessId" length="25" not-null="true" />
</natural-id>
<property name="Description" length="75" not-null="true" />
<set access="field.camelcase-underscore" cascade="all-delete-orphan" inverse="true" name="Allocations">
<key foreign-key="Allocations_Resource_FK">
<column name="ActivityId" />
</key>
<one-to-many class="Allocation />
</set>
<subclass name="Account" discriminator-value="ACCOUNT" />
<subclass name="SProject" discriminator-value="PROJECT" />
Here is the way I finally got both columns to be part of the same unique index:
<discriminator type="System.String" >
<column name="ActivitySubjectType" unique-key="ActivitySubjectTypeBusinessId" />
</discriminator>
<property name="BusinessId" length="25" not-null="true" node="1" unique-key="ActivitySubjectTypeBusinessId"/>
Don't use natural ids for primary keys. I would use a synthetic key, a database generated identity or a GUID for the primary key and add a unique constraint on the combination of BusinessId and ActivitySubjectType.

NHibernate Many-to-Many Mapping not working

I have a Nhibernate mapping file for a simple user/role mapping.
Here are the mapping files:
Users.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Sample.Persistence" namespace="Sample.Persistence.Model">
<class name="User" table="Users">
<id name="UserKey">
<generator class="identity"/>
</id>
<property name="UserName" column="UserName" type="String" />
<property name="Password" column="Password" type="Byte[]" />
<property name="FirstName" column="FirstName" type="String" />
<property name="LastName" column="LastName" type="String" />
<property name="Email" column="Email" type="String" />
<property name="Active" column="Active" type="Boolean" />
<property name="Locked" column="Locked" type="Boolean" />
<property name="LoginFailures" column="LoginFailures" type="int" />
<property name="LockoutDate" column="LockoutDate" type="DateTime" generated="insert" />
<property name="Expired" column="Expired" type="Boolean" generated="insert"/>
<set name="Roles" table="UsersRolesBridge" lazy="false">
<key column="UserKey" />
<many-to-many class="Role"
not-found="exception"
column="RoleKey" />
</set>
</class>
</hibernate-mapping>
Role.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Sample.Persistence" namespace="Sample.Persistence.Model">
<class name="Role" table="Roles">
<id name="RoleKey">
<generator class="identity"/>
</id>
<property name="Name" column="Name" type="String" />
<set name="Users" inverse="true" table="UsersRolesBridge" lazy="false" >
<key column="RoleKey" />
<many-to-many class="User" column="UserKey" />
</set>
</class>
</hibernate-mapping>
I am able to retrieve roles for each user via NHibernate but when I go to save a new object, the roles are not saved in the Bridge table.
The user is created and insert with no issues. I've checked that the Role collection, a field on the user, is being populated with the proper rolekey before the Session.Save() is called.
There is no exception thrown as well.
UPDATE:
After adding a cascade, there was still no actual insert taking place to the M-2-M table.
The logs from Nhibernate showed the following:
12/22/2010 23:18:11.684 [11] INFO NHibernate.Engine.Cascade
Message: processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
Exception:
12/22/2010 23:18:11.686 [11] INFO NHibernate.Engine.Cascade
Message: done processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
Exception:
12/22/2010 23:18:11.789 [11] INFO NHibernate.Engine.Cascade
Message: processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
Exception:
12/22/2010 23:18:11.792 [11] INFO NHibernate.Engine.Cascade
Message: cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for collection: Sample.Persistence.Model.User.Roles
Exception:
12/22/2010 23:18:11.814 [11] INFO NHibernate.Engine.Cascade
Message: done cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for collection: Sample.Persistence.Model.User.Roles
Exception:
12/22/2010 23:18:11.814 [11] INFO NHibernate.Engine.Cascade
Message: done processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
You haven't specified a cascade on the relationship. By default, inserts, updates, and deletes are not cascaded. More information here:
http://ayende.com/Blog/archive/2006/12/02/nhibernatecascadesthedifferentbetweenallalldeleteorphansandsaveupdate.aspx
I generally find that many to many issues are caused by not setting the cascade properties properly.
Try setting the cascade = "save-update" attribute on the Roles set in the Users.hbm.xml file
<set name="Roles" table="UsersRolesBridge" lazy="false" cascade="save-update">

Joining NHibernate Classes that share a common column but no foreign key

I have a couple of tables that I want to map to classes. The tables look like this:
Asset
---------
AssetId
AssetName
Product
---------
ProductId
ProductName
AssetId
Disposal
---------
DisposalId
AssetId
DisposalDate
Basically what I want to do is join the Product Table to the Disposal table on AssetId so that my Product has a collection of Disposals joined by asset. I have defined the following mapping but NHibernate (1.2) seems to ignore the key column defined in the bag and chooses to join the Product table to the Disposal table by ProductId (ie Product.ProductId = Disposal.AssetId). I'm not sure if this is a bug or if I'm not defining it properly but if anyone has a way to do this I'd be most greatful.
<class name="Product" table="Product" lazy="false">
<id name="ProductId" column="ProductId" type="int">
<generator class="native" />
</id>
<property name="ProductName" column="ProductName"/>
<bag name="Disposals" fetch="join" >
<key column="AssetId" foreign-key="AssetId/>
<many-to-many class="Disposal"/>
</bag>
</class>
Have you got your Disposals mapped to Products?
Your schema doesn't unique relate a Disposal to a Product. A Disposal can only relate to an Asset, not a Product.
You schema says to me that an Asset has many Products, and an Asset has many Disposals. There's nothing that says a Disposal is for a particular product.
The clean way:
<class name="Product" table="Product" lazy="false">
<id name="ProductId" column="ProductId" type="int">
<generator class="native" />
</id>
<property name="ProductName" column="ProductName"/>
<many-to-one name name="Asset" class="Asset" column="AssetId" />
</class>
<class name="Asset">
<id name="AssetId" >
<generator class="native" />
</id>
<property name="AssetName" />
<bag name="Disposals">
<key column="AssetId" />
<many-to-many class="Disposal" />
</bag>
</class>
foreign-key is used for DDL, I think it is the name of the foreign key constraint generated by schema export.
You can try property-ref, not completely sure if it works:
<class name="Product" table="Product" lazy="false">
<id name="ProductId" column="ProductId" type="int">
<generator class="native" />
</id>
<property name="ProductName" column="ProductName"/>
<property name="AssetId" />
<bag name="Disposals" fetch="join" >
<key column="AssetId" property-ref="AssetId/>
<one-to-many class="Disposal"/>
</bag>
</class>

Categories

Resources