I've have started my foray into C#.NET and NHibernate and I'm finally stuck on an exception I can't seem to figure out, and Google isn't helping.
I'm getting a "NHibernate.DuplicateMappingException : Duplicate class/entity mapping" on my Parent class. Below is my mapping file for the Parent class, and the Youth class that uses the Parent class:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Surrix.Cerberus.YouthData"
namespace="Surrix.Cerberus.YouthData.Domain">
<class name="Parent">
<id name="parentId">
<generator class="guid" />
</id>
<property name="firstName" not-null="true" />
<property name="lastName" not-null="true" />
<property name="homePhone" />
<property name="parentEmail" />
<property name="relationshipToYouth" />
<!-- Address component that should map to the Address class -->
<component name="parentAddress">
<property name="street" />
<property name="state" />
<property name="zipCode" />
<property name="city" />
</component>
</class>
</hibernate-mapping>
And here is the relevant part of the Youth class (it is considerably bigger)
<set name="YouthParents" table="YouthParents" cascade="none">
<key column="youthId" />
<many-to-many column="parentId" class="Parent"/>
</set>
Only other thing is the Youth class also has the firstName and lastName properties, but I can't see that being a problem.
Make sure you are not doing both of these 2 things.
(1) adding the assembly in code:
// Code Configuration
var cfg = new Configuration();
cfg.Configure();
cfg.AddAssembly(typeof(Employee).Assembly);
// Presuming Employee resides in "MyAssembly" as seen below.
(2) And then also adding the assembly in the config file:
<!-- .config configuration -->
<session-factory>
<!-- bunch of other stuff -->
<mapping assembly="MyAssembly"/> <!-- as in MyAssembly.dll -->
</session-factory>
You are adding the file or assembly containing the mapping twice to your Configuration object.
I had this problem, and solved it by putting this statement in hibernate.cfg.xml file:
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
Another possible cause to generate this error is multiple hbm files referencing the same Assembly during a Configuration.AddAssembly.
All hbm files in the same assembly are processed with one AddAssembly call.
As it gives a duplicate class entity mapping I can only imagen that you have two or more *.xml.hbm files referring to the same .net class.
Make sure that the xml class element for your Youth class does not have the same value for the name attribute.
Related
Having trouble with nHibernate mapping my enum to the DB.
Tried looking at the mapping but can't see anything wrong, my suspicions by this point are that it may have something to do with the enum?
Here's Fortron.Crm.Domain.Policy:
namespace Fortron.Crm.Domain
{
[Serializable]
public class Policy
{
private PolicyStatus _policyStatus;
public PolicyStatus PolicyStatus
{
get { return _policyStatus; }
set { _policyStatus = value; }
}
}
Here's the class mapping
<class name="Fortron.Crm.Domain.Policy, Fortron.Crm.Domain" table="Policy" lazy="false">
<id name="Id" access="nosetter.camelcase-underscore" column="PolicyId" unsaved-value="-1">
<generator class="identity" />
</id>
<set name="Claims" access="field.camelcase-underscore" inverse="true" lazy="false" cascade="save-update">
<key column="PolicyId" />
<one-to-many class="Fortron.Crm.Domain.Claim, Fortron.Crm.Domain" />
</set>
<many-to-one name="Product" column="ProductId" access="nosetter.camelcase-underscore" />
<property name="PolicyNumber" />
<property name="VehicleRegistrationNumber" />
<property name="ContractNumber" />
<property name="ContractPaymentAuthorised" />
<property name="ContractPaymentAuthorisedAt" />
<component name="Contact" access="nosetter.camelcase-underscore">
<property name="Title" />
<property name="GivenNames" />
<property name="Surname" />
<property name="BusinessName" />
<property name="DateOfBirth" />
<property name="Gender" column="GenderId" />
<property name="TelephoneNumber" />
<property name="MobileTelephoneNumber" />
<property name="WorkTelephoneNumber" />
<component name="Address" access="nosetter.camelcase-underscore">
<property name="StreetLine1" column="StreetLine1" />
<property name="StreetLine2" column="StreetLine2" />
<property name="CityTown" column="CityTown" />
<property name="Postcode" column="Postcode" />
<many-to-one name="StateTerritory" column="StateTerritoryId" />
</component>
</component>
<property name="CustomerNumber" column="CustomerNumber" not-null="false" />
<property name="Vin" column="Vin" not-null="false" />
<property name="PolicyStatus" column="PolicyStatusId" />
</class>
Lastly here's the stack trace:
Service cannot be started. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> NHibernate.MappingException: Could not compile the mapping document: Fortron.Crm.Domain.Policy.hbm.xml ---> NHibernate.MappingException: Problem trying to set property type by reflection ---> NHibernate.MappingException: class Fortron.Crm.Domain.Policy, Fortron.Crm.Domain, Version=2.0.0.1, Culture=neutral, PublicKeyToken=6f168f2566a816b4 not found while looking for property: PolicyStatus ---> NHibernate.PropertyNotFoundException: Could not find a getter for property 'PolicyStatus' in class 'Fortron.Crm.Domain.Policy'
at NHibernate.Properties.BasicPropertyAccessor.GetGetter(Type type, String propertyName)
at NHibernate.Util.ReflectHelper.ReflectedPropertyClass(String className, String name, String accessorName)
--- End of inner exception stack trace ---
at NHibernate.Util.ReflectHelper.ReflectedPropertyClass(String className, String name, String accessorName)
at NHibernate.Mapping....
Any ideas?
Change your member signature to make it virtual:
public virtual PolicyStatus PolicyStatus
NHibernate expects all the members of your entity to be virtual for binding. This is necessary because NHibernate creates proxy (to implement lazy loading) in certain cases. Without marked as virtual, this will not be possible for NHibernate.
You may refer this (or archive) article:
The quick answer to that question is: because we need members to be virtual in order to do our lazy loading magic/voodoo.
The longer answer is more interesting though. An important feature that any real ORM must have is transparent Lazy Loading. If you retrieve an object through an ORM, you don't want it to automatically pull in an entire object graph (not by default anyway), yet you don't want to litter your code with checks to see if certain associations have been loaded yet, and then loading them if necessary. This is the ORM's responsibility. Ideally, you want to be able to access properties and have the ORM load the necessary data upon first access of those properties if the data hasn't been retrieved yet.
NHibernate has this ability, yet it doesn't require you to inherit from some kind of NHibernate base class or implement any interfaces or anything like that. So how does it work? Well, NHibernate uses proxies of your classes at runtime whenever lazy loading is required. Ok, so what exactly is a proxy? In this case, an NHibernate proxy is a type which is generated dynamically when NHibernate is initialized for your application (this only happens once upon application startup). A proxy type will be generated for each of your entities that hasn't explicitly been mapped to avoid lazy loading (more on this later). A proxy type for one of your entities will actually inherit from your entity, and will then intercept each possible call you can perform on that type.
I have two classes: Order and User
Order has User class inside of it:
[DataMember]
public virtual User User { get; set; }
I have NHibernate mapping for both of them:
For User:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Purchasing" namespace="Purchasing.Other">
<class name="User" table="tUser">
<id name="RID">
<column name="RID" sql-type="bigint"/>
<generator class="native"/>
</id>
<property name="Created"/>
<property name="Modified"/>
<property name="UserName"/>
<property name="Email"/>
<property name="ExternalUserId"/>
</class>
</hibernate-mapping>
And I tried to add User class to Order mapping, BUT looks like something is wrong:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Purchasing" namespace="Purchasing.Other">
<class name="Order" table="tOrder">
<id name="Id">
<column name="OrderId" sql-type="bigint"/>
<generator class="native" />
</id>
<property name="DataOwnerId" />
<property name="UserId" />
<property name="OrderNo"/>
<property name="FirstTaken"/>
<property name="DateRequired" />
<property name="ExternalOrderId" />
<many-to-one name="User" class="User" property-ref="ExternalUserId">
<column name="UserId" not-null="false"/>
</many-to-one>
</class>
</hibernate-mapping>
I have a situation, when, writing Order in tOrder database, I also need to write its User to tUser database.
But looks like it didn't work. (Order mapping without User map works fine).
How in this situation mapping should look correctly, and what am I missing?
P.S Sorry for my bad English.
If we want to persist User with its Order, we can use cascading setting:
5.1.10. many-to-one
An ordinary association to another persistent class is declared using a many-to-one element. The relational model is a many-to-one association. (It's really just an object reference.)
<many-to-one
name="PropertyName" (1)
...
cascade="all|none|save-update|delete" (4)
...
/>
(4) cascade (optional): Specifies which operations should be cascaded from the parent object to the associated object.
So the mapping could be like this:
<many-to-one name="User" class="User"
property-ref="ExternalUserId"
cascade="save-update"
column="UserId" not-null="false"
/>
Now, we only have to be sure, that we try to find a user (it could be existing)
var user = session.Get<User>(userId);
if(user == null)
{
user = new User { ... };
}
And then we can assing that user to the Order
var order = new Order { ... };
order.User = user;
and calling session to persist Order - user will be as well:
session.SaveOrUpdae(order);
NOTE: if you can reference user by its native ID ... it would be better. Mapping then would look like:
<many-to-one name="User" class="User"
cascade="save-update"
column="RID" not-null="false"
/>
Mapping with property-ref should be used exceptionally
I'm having an issue where I'm sending a message to nServiceBus with a message data field of variable length. nServiceBus is using nHibernate to create a table called
[NServiceBus].[PendingMentorEmailSagaData]
from a class called PendingMentorEmailSagaData . The message field is however getting set to nvarchar(255), I am looking for a way to have it set to nvarchar(MAX).
I've tried using an embedded hbm file, but am getting a "persistent class PendingMentorEmailSagaData not found" error.
This probably means I can't figure out what class to set the file too.
The hbm file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="PendingMentorEmailSagaData">
<id name="Id" />
<property name="OriginalMessageId" />
<property name="Originator" />
<property name="PendingMentorEmailCommandId" />
<property name="JobBoardCode" />
</class>
</hibernate-mapping>
PendingMentorEmailSagaData is the name of the saga's data class.
Create a PendingMentorEmailSagaData.hbm.xml file in the same project that the saga exists on, eg:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:nhibernate-mapping-2.2">
<class name="MY_NAMESPACE.PendingMentorEmailSagaData, MY_ASSEMBLY_NAME" table="PendingMentorEmailSagaData" dynamic-update="true" optimistic-lock="all">
<id name="Id" type="Guid">
<generator class="assigned" />
</id>
<property name="Originator" />
<property name="OriginalMessageId" />
<property name="LargeText" type="StringClob" />
</class>
</hibernate-mapping>
2. Mark that file as an Embedded Resource
I am trying to map a SQL Server database with nHibernate that is full of tables with varchar primary keys that are generated by external software and I need update/read (no insert) access.
I cannot find a way to get past the following error:
XXXX.Tests.GMCRepository_Fixture.Can_get_existing_GMC_by_parameter'
failed: NHibernate.MappingException :
XXXX.Domain.Mappings.GMC2.hbm.xml(4,6): XML validation error: The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'property' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'meta, subselect, cache, synchronize, comment, tuplizer, id, composite-id' in namespace 'urn:nhibernate- mapping-2.2'.
Research has suggested this error is relating to there not being a valid primary key (id) defined.
Mapping XML looks like:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="XXXX.Domain" namespace="XXXX.Domain" xmlns="urn:nhibernate-mapping-2.2" schema="GM.dbo">
<class name="GMC2" table="C2" lazy="true" >
<property name="PARAMETER">
<column name="PARAMETER" sql-type="varchar" not-null="true" />
</property>
...
<id name="Recid">
<column name="recid" sql-type="varchar" not-null="true" />
</id>
</class>
</hibernate-mapping>
Thanks for your help!
I believe that the convention is to have the ID mapping as the first thing under the class declaration.
Also as part of the Id mapping you need to specify the Generator of the ID. In your case I think you will need the assigned generator added to your ID mapping. Your class mapping will look something like this.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="XXXXCRMAPI.Domain" namespace="XXXXCRMAPI.Domain" xmlns="urn:nhibernate-mapping-2.2" schema="GoldMine.dbo">
<class name="GMContact2" table="CONTACT2" lazy="true" >
<id name="Recid">
<generator type="assigned" />
<column name="recid" sql-type="varchar" not-null="true" />
</id>
<property name="Accountno">
<column name="ACCOUNTNO" sql-type="varchar" not-null="true" />
</property>
...
</class>
</hibernate-mapping>
I'm trying to solve pretty easy problem. I want to establish connection to 2 totally different databases ( but both mysql ). Now I tried to solve this by creating multiple config files and then creating multiple sessions. Everything works until I reached relations.
I have 2 tables in 2 databases:
db1
- News
db2
- News_Authors
I added News to db1 config and News_Authors to db2 config. When I try to build simple one-to-one relation error, I receive:
An association from the table songs refers to an unmapped class: db1.News_Authors
News.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="project.News, project" table="news" lazy="false">
<id name="id" column="id" type="integer" length="11">
<generator class="native" />
</id>
<property name="title" type="String" length="255" />
<property name="authorid" type="integer" length="5" />
<one-to-one name="NewsAuthor" cascade="all-delete-orphan" lazy="proxy" column="authorid" unique="true" />
</class>
</hibernate-mapping>
News_Authors.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="project.News_Authors, project" table="news_authors" lazy="false">
<id name="id" column="id" type="integer" length="11">
<generator class="native" />
</id>
<property name="name" type="String" length="255" />
</class>
</hibernate-mapping>
config
I added this to enable mappings. Now If I set both in one config files, everything works...
<mapping resource="project.News.hbm.xml" assembly="project" />
So how could I during creating of session also "notify" nhibernate that I have multiple sessions? Or should I pick totally another approach?
One other possible solution would be to create views in one of the sql server databases to reference the tables in the other. Views map just like tables and its easy to do a view that returns something like:
select * from db2.News_Authors
in the db1 database.
this way you would only need a single .hbm.xml file that maps to one of the two databases.
Hope this helps,
-Max
What you are after is not multiple sessions but multiple session factories. See this for more details.
The key here is that you don't have to initialize your session factory through config file - you can do it programatically. And it's just a step to have two session factories.