define NHibernate mappings in different assembly than the domain objects - nhibernate-mapping

I have an assembly Foo.Bar that holds all my domain logic and my domain object (e.g. public class Project, public interface IProjectRepository).
I also have an assembly Foo.Bar.Data that acts as my NHinbernate data access layer wich holds the IProjectRepository NHibernate-based implementation. It references Foo.Bar and contains the mapping files such as Project.hbm.xml.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Foo.Bar"
namespace="Foo.Bar.Domain" default-lazy="false">
<class name="Project">
<id name="Id" column="ID" />
<property name="Customer" column="CUSTOMER" />
</class>
</hibernate-mapping>
Unfortunately with this setup I get the following exception:
NHibernate.MappingException : No persister for: Foo.Bar.Domain.Project
And the NHibernate log says:
WARN NHibernate.Cfg.Configuration (null) - No mapped documents found in assembly: Foo.Bar, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Howver if I add the mapping file to the Foo.Bar assmbly everything works fine. But I really do not want any data access or NHinbernate implementation details into my domain assembly. This tends to be best practise, right? Is this kind of setup not a good way to go?
How can I make NHibernate find the mapping files in the Foo.Bar.Data* assembly?

Turns out that all I have to do is add
<mapping assembly="Foo.Bar.Data"/>
to my hibnerate configuration within the <session-factory> element.

Related

NHibernate ignoring GeneratedBy.Identity()

I'm attempting to use NHibernate to serialize a moderately complex object graph*
Actual mapping was done via FNH, but I've dumped the HBM files and confirmed that the generated XML conforms to NHibernate conventions.
Here's a snippet of the HBM, just for grins:
<class xmlns="urn:nhibernate-mapping-2.2" schema="obsv" optimistic-lock="version" name="Spc.Ofp.Tubs.DAL.Entities.PurseSeineActivity, TubsDAL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="s_daylog">
<id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="s_daylog_id" not-null="true" />
<generator class="identity" />
</id>
This mapping results in the following SQL (via SQL debug, snipped for readability):
INSERT INTO obsv.s_daylog (/* columns 0 thru 20 snipped */s_daylog_id /* <-- PK from mapping! */)
VALUES (/* parameters snipped */#p21);
select SCOPE_IDENTITY();#p21 = NULL [Type: Int32 (0)]
I believe that the presence of the "select SCOPE_IDENTITY();" text confirms that
NHibernate partially understands what should happen. I just don't understand why it's writing the PK column into the insert query.
I've been using the mappings for reading the graph just fine, so I'm fairly certain this isn't a basic mapping issue.
FWIW, Cascade is set to None (for other reasons, I need to work with these entities without
ramifications up and down the object graph).
*By moderately complex, I mean I have a object which has between 6 and 10 properties which are lists of child entities. A good number of those child entities also have child entities. In the most complex case, there are 5 generations of entities under the root entity.
I think it is because your mapping seems incorrect.
According to the NHibernate Reference, the id tag has a "column" attribute to set the column name, not a child element.
Try using dynamic-insert="true":
<class xmlns="urn:nhibernate-mapping-2.2" dynamic-insert="true" schema="obsv" optimistic-lock="version" name="Spc.Ofp.Tubs.DAL.Entities.PurseSeineActivity, TubsDAL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="s_daylog">

Using nHibernate secondary cache for a many-to-one reference

I'm trying to get nHibernate to use second-level cache with a many-to-one relationship, however I can't find any clear explanation on how to set it up correctly. I found this How to get nhibernate to cache tables referenced via many-to-one - is my config correct?, but the example sJHonny provided is for one-to-many and it's not working for me when I adopt it. There are other posts going over this subject, but none of them are specific enough.
The XML config I provide works (I had to edit dramatically, so "hopefully" works), but lookup objects are being cached only when they are retrieved as DataObject is queried. I'd like to know 1) where/how to preload the LookupObject collection? 2) what if I assign this collection to a region and set expiration, then where/how do I reload the cache again? 3) how to change the DataObject's hbm.xml such that nHibernate doesn't generate a join with the LOOKUP table, i.e. such that lookup objects always come from the secondary cache, only getting the DATA.LOOKUP_ID from the db, not the LOOKUP.NAME?
LookupObject.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="BusinessObjects" assembly="BusinessObjects">
<class name="LookupObject" table="LOOKUP" mutable="false" batch-size="20">
<cache usage="read-only" />
<id name="Id" column="ID" />
<property name="Name" column="NAME" />
</class>
</hibernate-mapping>
DataObject.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="BusinessObjects" assembly="BusinessObjects">
<class name="DataObject"
table="DATA" mutable="false">
<composite-id>
<key-property name="Id" column="ID"/>
<key-property name="Date" column="DATE" type="Date"/>
</composite-id>
<many-to-one name="LookupObject" not-null="true" column="LOOKUP_ID" fetch="join">
</class>
</hibernate-mapping>
I believe I answered my own questions. 1) For preloading the entire collection, I just needed to change my code such that the entire list of lookups is always pulled from the DB and cached, then I get the individual object by ID with LINQ. 2) Same as before. 3) I haven't tested this yet, but I need to remove fetch="join" from many-to-one element because otherwise the SQL join will still be generated and data will be still returned. Then set the lookup object without nHibernate by getting it from cache.

NHibernate Mapping Problem - Can I Map Multiple Assemblies?

I have the following mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Project1.Accounts"
namespace="Project1.Core.Domain">
<class name="Equipment" table="Equipment">
<id name="ID" column="ID">
<generator class="identity"></generator>
</id>
<property name="Name" />
<property name="Description" />
<property name="AccountID" />
<property name="EquipmentTypeID" />
<many-to-one name="Account" class="Project2.Core.Domain.Account, Project2.Core" column="AccountID"/>
<many-to-one name="EquipmentType" class="Insight.IT.Accounts.Core.Domain.EquipmentType, Insight.IT.Accounts" column="EquipmentTypeID"/>
</class>
</hibernate-mapping>
I'm getting the following error:
NHibernate.MappingException: An association from the table Equipment refers to an unmapped class: Project2.Domain.Account
Just to be clear - The Account class lives in a different assembly than the Equipment class does. The project that the Account class resides in has it's own hibernate.cfg.xml.
Basically, it looks like i need a way to reference multiple mapping assemblies in the hibernate.cfg.xml file. Is this possible??
You can do it like this:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">...</property>
<property name="connection.driver_class">...</property>
...
<mapping assembly="MyProject.OtherAssembly"/>
</session-factory>
</hibernate-configuration>
It is possible, you'll just have to indicate this in your mapping file (and it looks like you did this).
Did you add both the assemblies to the NHibernate configuration, before creating the sessionfactory ?
Thanks for the responses. Actually, I ended up doing it a little different. I just removed the tag from the hibernate.cfg and used fully qualified names in the .hbm.xml files. Thanks!!

Polymorphic NHibernate mappings

I have an interface IUserLocation and a concrete type UserLocation.
When I use ICriteria, specifying the interface IUserLocation, I want NHibernate to instantiate a collection of the concrete UserLocation type.
I have created an HBM mapping file using the table per concrete type strategy (shown below). However, when I query NHibernate using ICriteria I get:
NHibernate cannot instantiate abstract class or interface MyNamespace.IUserLocation
Can anyone see why this is? (source code for the relevant bit of NHibernate here (I think))
My ICriteria:
var filter = DetachedCriteria.For<IUserLocation>()
.Add(Restrictions.Eq("UserId", userId));
return filter.GetExecutableCriteria(UoW.Session)
.List<IUserLocation>();
My mapping file:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="MyNamespace.IUserLocation,MyAssembly" abstract="true" table="IUserLocations">
<composite-id>
<key-property name="UserId" column="UserId" type="System.Guid"></key-property>
<key-many-to-one name="Location" column="LocationId" class="MyNamespace.ILocation,MyAssembly"></key-many-to-one>
</composite-id>
<union-subclass table="UserLocations" name="MyNamespace2.UserLocation,MyAssembly2">
<property name="IsAdmin" />
</union-subclass>
</class>
</hibernate-mapping>
From the documentation it looks like your mapping file should do it to me. I've never tried table per concrete class though. And I notice the examples in the NHibernate documentation for it don't use interfaces for the base class. Perhaps it's not supported?
I have used Table per concrete class, using implicit polymorphism before with a separate mapping for each subclass.
<class name="MyNamespace.UserLocation,MyAssembly" table="UserLocations">
...
</class>

Newbie problem with NHibernate version 2.1.2.4000 Mapping

I have what appears to be a simple mapping problem in NHibernate, however I have been struggling to find a solution to the problem for a number of days now, and would appreciate some assistance. I am using VB.NET under VS2005. My VS2005 solution structure is as follows:
Solution: PsalertsIP
Project (Assembly): Core
Folder Data (Namespace PsalertsIp.Core.Data)
Contains Interfaces for communication with repository classes
example: PsalertsEventRepo Implements IPsalertsEventRepo
Folder Domain (Namespace PsalertsIP.Core.Domain)
Contains all POCO domain objects and related interfaces
example: PsalertsEvent Implements IPsalertsEvent
Also underneath the assembly 'Core' are the NHibernate config file and the mapping file for the PsalertsEvent class, which is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Core"
namespace="Core.Domain">
<class name="PsalertsEvent" table="Source_table" lazy="true">
<id name="Id" column="Id" type="long" unsaved-value="0"
access="field.camelcase-underscore">
<generator class="native" >
<param name="sequence">My_Oracle_Sequence</param>
</generator>
</id>
<property name="Substation" column="Field1" />
<property name="BusbarId" column="Field2" />
<property name="PlantId" column="Field3" />
<property name="AlarmName" column="Field4" />
<property name="AlarmStatus" column="Field5" />
<property name="EventTime" column="Field6" />
</class>
</hibernate-mapping>
When I attempt to carry out a simple test of the NHibernate environment through NUnit (appreciate that this isn't unit testing, however needed a simple vehicle to test the NHibernate setup), the test fails, and I observe the following output in NUnit:
PsalertsIp.Tests.Data.PSALERTSEventRepoTests (TestFixtureSetUp):
System.TypeInitializationException : The type initializer for 'Nested' threw an exception.
----> NHibernate.MappingException : Could not compile the mapping document: PsalertsEvent.hbm.xml
----> NHibernate.MappingException : persistent class Core.Domain.PsalertsEvent, Core not found
----> System.TypeLoadException : Could not load type 'Core.Domain.PsalertsEvent' from assembly 'Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
I suspect that the problem may be to do with the structure of the solution in VS2005, however I have tested multiple different assembly/namespace permutations to no avail.
I think you need to change the namespace attribute on the hibernate-mapping element to "PsalertsIP.Core.Domain" (as you've specified above).
Also ensure the assembly attribute on the hibernate-mapping element specifies the full assembly name of your project (right-click project -> Properties -> Application tab).
hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Core"
namespace="PsalertsIP.Core.Domain">