I have a one-to-one relationship between a Person class and an Employee. I expect the INSERT to cascade from the Person to the Employee. However, this does not happen. I've tried cascade='all' and cascade='save-update' on one-to-one relationship element, but it didn't work. I have also uploaded my entire source code on: http://bit.ly/gnkxBr (3.52 MB)
The structures of the my objects are as follows:
public class Person
{
public virtual Employee Employee { get; set; }
public virtual int Age { get; set; }
public virtual string Forename { get; set; }
public virtual string Surname { get; set; }
public virtual int PersonID { get; set; }
}
public class Employee
{
public virtual int PersonID { get; set; }
public virtual string PayRollNo { get; set; }
public virtual int Holidays { get; set; }
public virtual Person Person { get; set; }
}
Mapping files shown below:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Person, Employee.DAL" table="`Person`" >
<id name="PersonID" column="`PersonId`" type="int">
<generator class="native" />
</id>
<property type="string" name="Forename" column="`Forename`" />
<property type="string" name="Surname" column="`Surname`" />
<property type="int" name="Age" column="`Age`" />
<one-to-one name="Employee" class="Employee" cascade="all" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Employee, Employee.DAL" table="`Employee`" >
<id name="PersonID" column="`PersonId`">
<generator class="foreign">
<param name="property" >Person</param>
</generator>
</id>
<property type="string" length="30" name="PayRollNo" column="`PayRollNo`" />
<property type="int" name="Holidays" column="`Holidays`" />
<one-to-one name="Person" class="Person" constrained="true" />
</class>
</hibernate-mapping>
Put the session.Save() method in a transaction. Or use the session.Flush() method after calling the save method.
Related
I am new to NHibernate. Just started learning NHibernate.
I am getting for run-time error with my C# code
NHibernate.PropertyAccessException was unhandled
Message=Invalid Cast (check your mapping for property type mismatches); setter of NHibernateDemo.Customer
InnerException: System.InvalidCastException
Message=Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericSet`1[NHibernateDemo.Order]' to type 'System.Collections.Generic.ISet`1[NHibernateDemo.Order]'.
Following is my C# code written for
public class Customer {
public Customer()
{
MemberSince = DateTime.UtcNow;
Orders = new HashSet<Order>();
}
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Location Address { get; set; }
public virtual ISet<Order> Orders { get; set; }
}
It's hbm file is as follow
<class name="Customer" table="Customer" lazy="true" >
<id name="Id">
<generator class="guid.comb" />
</id>
<property name="FirstName" />
<property name="LastName" />
<component name="Address" >
<property name="Street" />
<property name="City" />
<property name="State" />
<property name="Country" />
</component>
<set name="Orders" table="`Order`" order-by="Ordered desc">
<key column="CustomerId" />
<one-to-many class="Order" />
</set>
</class>
public class Order {
public virtual Guid Id { get; set; }
public virtual DateTime Ordered { get; set; }
public virtual DateTime Shipped { get; set; }
public virtual Location ShipAddress { get; set; }
public virtual Customer Customer { get; set; }
}
<class name="Order" table="`Order`">
<id name="Id">
<generator class="guid.comb" />
</id>
<property name="Ordered" />
<property name="Shipped" />
<component name="ShipAddress" >
<property name="Street" />
<property name="City" />
<property name="State" />
<property name="Country" />
</component>
<many-to-one name="Customer" column="CustomerId" />
</class>
If I change "set" section to "list" in Customer HBM file and do necessary changes in Customer class. Program is running correctly. Also if i remove "set" section from Customer HBM file; it is working.
Can you please help me to find what is wrong with "set" section of Customer HBM file?
Your mapping is almost correct, but the ISet interface is not from System namespace but from iesi library (distributed with NHibernate)
So you can reference iesi and change your mapping:
public virtual Iesi.Collections.Generic.ISet<Order> Orders { get; set; }
Or use the IList<>
and mapping with a bag
<bag name="Orders" table="`Order`" order-by="Ordered desc">
<key column="CustomerId" />
<one-to-many class="Order" />
</bag>
NOTE also do not forget to init the list, just in case that entity is created via new operator and NOT loaded by NHibernate
I'm confused with this situation. Having these two classes:
public class Payment {
public Payment() { }
public string Trn { get; set; }
public TxType TxTypeId { get; set; }
public string TxCode { get; set; }
public System.Nullable<decimal> Amount { get; set; }
public System.Nullable<System.DateTime> DateStamp { get; set; } }
public PaymentAudit() { }
public System.DateTime DateStamp { get; set; }
public Payment Trn { get; set; }
public PaymentSaga PaymentStateId { get; set; }
public ProcessState ProcessState { get; set; }
public PublishState PublishState { get; set; }
public System.Nullable<short> ChgCount { get; set; }
public string UserName { get; set; }
And mappings are:
<class name="Payment" table="Payment" lazy="false" >
<id name="Trn">
<generator class="identity" />
</id>
<many-to-one insert="false" update="false" lazy="false" name="TxTypeId">
<column name="TxTypeId" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="TxTypeId">
<column name="TxTypeId" sql-type="varchar" not-null="false" />
</property>
<many-to-one insert="false" update="false" lazy="false" name="TxCode">
<column name="TxCode" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="TxCode">
<column name="TxCode" sql-type="varchar" not-null="false" />
</property>
<property name="Amount">
<column name="Amount" sql-type="decimal" not-null="false" />
</property>
<property name="DateStamp">
<column name="DateStamp" sql-type="datetime" not-null="false" />
</property>
<bag name="PaymentAudits" inverse="true" cascade="none">
<key column="Trn" />
<one-to-many class="PaymentAudit" not-found="ignore" />
</bag>
</class>
<class name="PaymentAudit" table="PaymentAudit" >
<composite-id>
<key-many-to-one name="PaymentStateId" column="PaymentStateId" />
<key-property name="DateStamp" column="DateStamp" />
<key-many-to-one name="ProcessState" column="ProcessState" />
</composite-id>
<property name="PublishState">
<column name="PublishState" sql-type="varchar" not-null="false" />
</property>
<many-to-one name="Trn">
<column name="Trn" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="ChgCount">
<column name="ChgCount" sql-type="smallint" not-null="false" />
</property>
<property name="UserName">
<column name="UserName" sql-type="nvarchar" not-null="false" />
</property>
<many-to-one name="PublishState">
<column name="PublishState" sql-type="varchar" not-null="false" />
</many-to-one>
</class>
The tables,
Payment:
Trn PK
TxTypeId FK
TxCode FK
Amount
DateStamp
PaymentAudit:
PaymentStateId PK
DateStamp PK
ProcessState PK
PublishState FK
Trn FK
ChgCount
UserName
And I got this error:
Could not determine type for: MyProject.NHibernate.Payment.Model.Payment.Payment, MyProject.NHibernate.Payment.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(Trn)
Any ideas?
Thanks.
Trn is the primary key, is a string and is mapped as identity? Identity is an integer, so the property should be int.
I have such a simple model:
public abstract class Entity
{
public virtual Guid Id { get; protected set; }
}
public class Post : Entity
{
public String Title { get ; set; }
public String Content { get; set; }
public DateTime Timestamp { get; set; }
public Byte[] Thumbnail { get; set; }
}
public class Blog : Entity
{
public String Title { get; set; }
public ISet<Post> Posts { get; set; }
}
Then I have such mappings:
BLOG
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true assembly="Application.Domain" namespace="Application.Domain.Entities">
<class name="Blog">
<!-- id generator -->
<id name="Id">
<generator class="guid.comb" />
</id>
<!-- properties/columns -->
<property name="Title" not-null="true" />
<!-- components/columns -->
<!-- associations -->
<set name="Posts" cascade="all">
<!-- key column? -->
</set>
</class>
</hibernate-mapping>
POST
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" assembly="Application.Domain" namespace="Application.Domain.Entities">
<class name="Blog">
<!-- id generator -->
<id name="Id">
<generator class="guid.comb" />
</id>
<!-- properties/columns -->
<property name="Title" not-null="true" />
<property name="Content" not-null="true" />
<property name="Timestamp" not-null="true"/>
<property name="Thmbnail" />
<!-- components/columns -->
<!-- associations -->
</class>
</hibernate-mapping>
How do I map one-to-many association (unidirectional)?
Thanks!
In the blog mapping file you need to define a one-to-many relation between the foreign key column that references the Blog entity in the post table say it is BlogId and you need to tell'em what class this one-to-many relation relates to, in your case this will be the Post class and you need to define it with it's fully qualified namespace that contains this class and a comma then the assembly name as following:
<set name="Posts" table="Post">
<key column="BlogId"/>
<one-to-many class="Application.Domain.Entities.Post, Application.Domain"/>
</set>
I think it's the same problem as described here.
And also, I think you have a mistake in mapping of the Post - class name shouldn't be Blog. Also, there's no relation from Post to Blog in your example.
I have problem using Fluent Nhibernate, I have following model. When I try to save Hotel with has new Geography I getting foreign key exception, looks like Nhibenate fails to save data in correct order, is it something I can correct via Fluent Nhibernate ?
public class Geography {
public virtual int CityID { get; set; }
public virtual string CountryCode { get; set; }
}
public class Hotel
{
public virtual int HotelID { get; set; }
public virtual Geography City { get; set; }
}
Mapping
public class HotelMap : ClassMap<Hotel>
{
public HotelMap()
{
Id(x => x.HotelID)
.GeneratedBy
.Identity();
References(x => x.City, "CityId")
.Cascade.All();
}
}
public class GeographyMap : ClassMap<Geography>
{
public GeographyMap()
{
Id(x => x.CityID);
Map(x => x.CountryCode);
HasMany(a => a.Hotels)
.Cascade.All();
}
}
Added generated mappings
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Hotel" table="`Hotel`">
<id name="HotelID" type="System.Int32">
<column name="HotelID" />
<generator class="assigned" />
</id>
<many-to-one cascade="all" class="Geography" foreign-key="HotelGeography" name="City">
<column name="CityId" />
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Geography" table="`Geography`">
<id name="CityID" type="System.Int32">
<column name="CityID" />
<generator class="assigned" />
</id>
<bag cascade="all" inverse="true" name="Hotels" mutable="true">
<key>
<column name="HotelID" />
</key>
<one-to-many class="Hotel" />
</bag>
<property name="CountryCode" type="System.String">
<column name="CountryCode" />
</property>
</class>
</hibernate-mapping>
Try to specify identity generator for Geography class. If you want to have a database-generated unique identifier (like identity), you can accomplish that by adding .GeneratedBy.Native() to your Id(...) in GeographyMap class.
Add .Inverse() to the collection mapping.
For more details, have a look at http://nhibernate.info/doc/nhibernate-reference/collections.html#collections-onetomany and http://nhibernate.info/doc/nhibernate-reference/collections.html#collections-bidirectional
i have these 2 classes:
public class Category
{
IDictionary<string, CategoryResorce> _resources;
}
public class CategoryResource
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
and this is xml mapping
<class name="Category" table="Categories">
<id name="ID">
<generator class="identity"/>
</id>
<map name="Resources" table="CategoriesResources" lazy="false">
<key column="EntityID" />
<index column="LangCode" type="string"/>
<composite-element class="Aca3.Models.Resources.CategoryResource">
<property name="Name" column="Name" />
<property name="Description" column="Description"/>
</composite-element>
</map>
</class>
and i'd like to write it with Fluent.
I found something similar and i was trying with this code:
HasMany(x => x.Resources)
.AsMap<string>("LangCode")
.AsIndexedCollection<string>("LangCode", c => c.GetIndexMapping())
.Cascade.All()
.KeyColumn("EntityID");
but i dont know how to map the CategoryResource entity as a composite element inside the Category element.
Any advice ?
thanks
I think the mapping you're looking for is something like this:
HasMany<CategoryResource>(x => x._resources)
.AsMap<string>("LangCode")
.KeyColumn("EntityID")
.Table("CategoryResources")
.Component(x =>
{
x.Map(c => c.Name);
x.Map(c => c.Description);
})
.Cascade.All();