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
Related
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 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.
i want to convert this mapping file from NHibernate to Fluent NHibernate
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="CustomCollectionsBasic.Core.Category, CustomCollectionsBasic.Core" table="Categories">
<id name="ID" column="CategoryID" unsaved-value="0">
<generator class="identity" />
</id>
<property name="Name" column="CategoryName" />
<bag name="ProductsInCategory" table="Products" cascade="all" inverse="true"
collection-type="CustomCollectionsBasic.Data.Collections.PersistentProductsType, CustomCollectionsBasic.Data">
<key column="CategoryID" />
<one-to-many class="CustomCollectionsBasic.Core.Product,ustomCollectionsBasic.Core" />
</bag>
</class>
</hibernate-mapping>
I try to convert it by NHibernateHbmToFluent and the result is
public class CategoryMap: ClassMap<CustomCollectionsBasic.Core.Category>
{
public CategoryMap()
{
Table("Categories");
Id(x => x.ID)
.GeneratedBy.
.UnsavedValue(0);
Map(x => x.Name, "CategoryName");
HasMany<CustomCollectionsBasic.Core.Product>(x => x.ProductsInCategory)
.AsBag()
.KeyColumn("CategoryID")
.Table("Products")
.Inverse()
.Cascade;
}
}
but it not working.
Any ideas on how to map this?
Thanks in advance.
Id(x => x.ID)
.GeneratedBy
.Identity()
.UnsavedValue(0);
I think the other stuff is OK
What is fuentHibernate? Why is it used? What is the difference between Hibernate and Fluent Hibernate?
Fluent NHibernate offers an alternative to NHibernate's standard XML mapping files. Rather than writing XML documents (.hbm.xml files), Fluent NHibernate lets you write mappings in strongly typed C# code. This allows for easy refactoring, improved readability and more concise code.
Traditional HBM XML mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="QuickStart" assembly="QuickStart">
<class name="Cat" table="Cat">
<id name="Id">
<generator class="identity" />
</id>
<property name="Name">
<column name="Name" length="16" not-null="true" />
</property>
<property name="Sex" />
<many-to-one name="Mate" />
<bag name="Kittens">
<key column="mother_id" />
<one-to-many class="Cat" />
</bag>
</class>
</hibernate-mapping>
Fluent NHibernate equivalent
public class CatMap : ClassMap<Cat>
{
public CatMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Length(16)
.Not.Nullable();
Map(x => x.Sex);
References(x => x.Mate);
HasMany(x => x.Kittens);
}
}
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();