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">
Related
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.
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!!
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>
I have an application that has a core assembly with base classes that I need to inherit from.. I need to save these to the database and after reading about NHibernate decided to use it.
However I have a problem with one of my new inherited classes.. I have setup the subclass map but when I save, it neither attempts to save any of it's base class properties or any of it's new ones that I have assigned in the mapping!
My classes are laid out like the following: (from a small demo app)
core assemblies
DataItem -> User
Anything that will touch the database inherits the DataItem class as it handles the id, modified date etc etc..
In my test I setup user to only have a FirstName..
If I save a new User it works great.. however when I inherit from user and then add another property called LastName and attempt to save this new object.. it only puts a sql statement together of INSERT INTO t_User (id) VALUES(?).. it doesn't attempt to save the first name or last name.. either though both have been set and are mapped.
My nhibernate.config:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory name="DAL">
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="adonet.batch_size">16</property>
<property name="current_session_context_class">web</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<mapping assembly="DAL"/>
<mapping assembly="NHibernateDemo"/>
</session-factory>
</hibernate-configuration>
As you can see I have 2 assemblies.. my DAL is my core and the NHibernateDemo is a web application that uses the core for inheritance.
My core DataItem mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DAL" namespace="DAL.Model">
<class name="DataItem" table="t_DataItem" >
<id name="Id">
<generator class="native" />
</id>
<discriminator column="typeid" type="System.Int32"></discriminator>
<property name="IsActive" column="isActive" not-null="true" />
<property name="TypeId" column="typeId" not-null="true"></property>
<many-to-one name="Parent" column="ParentId" class="DataItem"></many-to-one>
<bag name="Children" cascade="all-delete-orphan">
<key column="ParentId"></key>
<one-to-many class="DataItem"/>
</bag>
<joined-subclass name="User" table="t_Users">
<key column="id"></key>
<property name="FirstName" column="firstName" not-null="true" ></property>
</joined-subclass>
<joined-subclass name="Email" table="t_Emails">
<key column="emailid"></key>
<property name="Address" column="Address"></property>
</joined-subclass>
</class>
</hibernate-mapping>
My inherited NewUser mapping that doesn't work!:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateDemo" namespace="NHibernateDemo.Model">
<subclass name="NewUser" extends="DAL.Model.User, DAL" discriminator-value="1">
<property name="LastName" column="LastName"></property>
</subclass>
Why is it that when I attempt to save my class NewUser that it doesn't attempt to save any of the other properties set, whether from it's base or newly declared properties?
I'd really appreciate any help or insight to this.. I must be missing something really simple and I just can't see it.
Thanks,
Mike
It could be that you're not able to mix subclass and joined-subclass mappings in the same class hierarchy. Since your DataItem and User are related by joined-subclass, you may need to make your NewUser class another joined-subclass of User.
Another issue might be the use of the "discriminator" in your current NewUser mapping. The discriminator should be an additional column in your User table that NHibernate uses to tell the difference between a User record and a NewUser record. I'm not sure if this works where the User base class might not specify its discriminator value, whereas the NewUser does. I'm not sure if you're specifying the discriminator-column anywhere, which might also be a problem.
I would suggest first trying to make NewUser a joined-subclass of User.
I have the following solution project structure:
Application.Core.Entities
Application.Xtend.CustomerName.Entities
In the Core project I have an entity Customer defiend. In the XTend project, I have an entity defined that subclasses Customer named xCustomer (for lack of a better name at this time...).
The idea here is that we have a Core domain model in our application. A customer can then create a new assembly that contains extensions to our core model. When the extension assembly is present a smart IRepository class will return a subclass of the core class instead.
I am attempting to map this relationship in NHibernate. Using Fluent NHibernate I was able to generate this mapping:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
default-lazy="false"
assembly="NHibernate.Core.Entites"
namespace="NHibernate.Entites"
default-access="field.camelcase-underscore">
<!-- Customer is located in assembly Application.Core.Entities -->
<class name="Customer" table="Customers" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="Id" type="Int64">
<generator class="native" />
</id>
<component name="Name" insert="true" update="true">
<property name="LastName" column="LastName" length="255" type="String" not-null="true">
<column name="LastName" />
</property>
<property name="FirstName" column="FirstName" length="255" type="String" not-null="true">
<column name="FirstName" />
</property>
</component>
<!-- xCustomer is located in assembly Application.XTend.CustomerName.Entities -->
<joined-subclass name="xCustomer" table="xCustomer">
<key column="CustomerId" />
<property name="CustomerType" column="CustomerType" length="255" type="String" not-null="true">
<column name="CustomerType" />
</property>
</joined-subclass>
</class>
</hibernate-mapping>
But NHib throws the following error:
NHibernate.MappingException:
persistent class
Application.Entites.xCustomer,
Application.Core.Entites not found
---> System.TypeLoadException: Could not load type
'Application.Entites.xCustomer' from
assembly 'Application.Core.Entites,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'..
Which makes sense xCustomer is not defined in the Core library.
Is it possible to span different assemblies like this? Am I approaching the problem wrong?
I asked this same question on the NHibernate Users mailing list and the solution was so obvious that I am somewhat embarrassed that I couldn't see it.
The hibernate-mapping attributes assembly and namespace are convenient short cuts that allow you to not have to fully qualify your class names. This lets you have the nice mark up , but the name attribute of both class and joined-subclass elements can take a fully qualified assembly name as well.
So the above broken mapping file can be fixed like so:
<joined-subclass name="Application.XTend.CustomerName.Entities.xCustomer,
Application.XTend.CustomerName.Entities, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null"
table="xCustomer">
<key column="CustomerId" />
<property name="CustomerType" column="CustomerType" length="255"
type="String" not-null="true">
<column name="CustomerType" />
</property>
</joined-subclass>
This works as I expected it to. So I then took a look at the Fluent-NHibernate source and created a patch complete with working unit tests to resolve the issue and submitted it to the project.
Thanks for you help #David Kemp
You need to map using the extends attribute of the <class> element (AFAIK, this is new in NHibernate 2.0). Then you can have your subclass mapping (.hbm.xml) in the XTend assembly.
You might have to use the AddAttribute/AddProperty (can't remember what it's called) to do this using Fluent NHibernate. (Or submit a patch).