nhibernate mapping, many to one - nhibernate

I have entity house which have besides other properties list of images.
Every image will be uploaded as a single action, one by one using js crop techniques.
Updated:
So one house can have many images.
public House
{
public Guid Id {get; set;}
....
List<Image> Images {get; set;}
}
public class Images
{
public House House {get; set;}
public string Path {get;set;}
public string Name {get;set;}
public string Description {get;set;}
}
My db tables are following:
House
Id
Name, ...
On this side I don't have relation to the Image table
Image table
Id
HouseId
Path
Name
Description
Is this approach ok?
How to map these objects using nhibernate orm?
Thanks

Declare your entities, and then in mapping files just associate Image HouseId with entity of House class. You should have foreign key in db. As xml answer already here, I'd add fluent nhibernate way.
public class House
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
....
}
public class Image
{
public virtual int Id { get; set; }
public virtual House HouseEntity { get; set; }
....
}
public class HouseMap : ClassMap<House>
{
public HouseMap()
{
Table("House");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name);
.....
}
}
public class ImageMap : ClassMap<Image>
{
public ImageMap()
{
Table("Image");
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.House);
//References(x => x.House, "foreignKeyName"); There is also way to reference with specifying foreign key field
Map(x => x.Name);
....
}
}

House.hbm.xml:
<bag name="Images" cascade="all-delete-orphan" inverse="true">
<key column="HouseId" />
<one-to-many class="Image" />
</bag>
Image.hbm.xml:
<id type="int" column="Id">
<generator ... />
</id>
<many-to-one name="House" column="HouseId" cascade="none" />
Since you have a primary key in the images table and no Id property in your domain object, the id-mapping should be without the name-attribute.

Related

Issue when quering Hierarchy to Hierarchy relationship

A Teacher has a one-to-one with a Student.
A SpecialTeacher extends Teacher but deals specifically with SpecialStudents.
Using table per class in the hierarchies.
public class Teacher
{
public virtual int Id { get; set; }
public virtual int DepartmentId { get; set; }
public virtual String Name { get; set; }
public virtual Student Student { get; set; }
}
public class SpecialTeacher : Teacher
{
public virtual string TelephoneNumber { get; set; } //SpecialTeachers get to have a phone
public virtual SpecialStudent SpecialStudent { get { return (SpecialStudent)base.Student; } set { Student = value; } }
}
public class Student
{
public virtual int Id { get; set; }
public String Name { get; set; }
}
public class SpecialStudent : Student
{
public int SpecialMark { get; set; }
}
and the associated mappings:
<class name="Student">
<id name="Id" />
<property name="Name" />
</class>
<joined-subclass name="SpecialStudent" extends="Student">
<key column="Id" />
<property name="SpecialMark" />
</joined-subclass>
<class name="Teacher">
<id name="Id" />
<property name="DepartmentId" />
<property name="Name" />
<many-to-one name="Student" column="StudentId" />
</class>
<joined-subclass name="SpecialTeacher" extends="Teacher">
<key column="Id" />
<property name="TelephoneNumber" />
</joined-subclass>
So, let's say that we want to get the average mark for SpecialStudents for a given department:
public double GetAverageScoreForSpecialStudentsByDepartment(int departmentId)
{
return CurrentSession.Query<SpecialTeacher>()
.Where(st => st.DepartmentId == departmentId)
.Average(ss => ss.SpecialStudent.SpecialMark);
}
The test will fail because it will complain that SpecialStudent is not a mapped property of SpecialTeacher.
The only way that I can think of avoiding this issue is to map the property, but this is duplication since the base Teacher is already mapped to the Student hierarchy.
Update
I meant to also mention that previously we had the SpecialTeacher set up like:
public class SpecialTeacher : Teacher
{
public virtual string TelephoneNumber { get; set; } //SpecialTeachers get to have a phone
public virtual new SpecialStudent Student { get { return (SpecialStudent)base.Student; } set { Student = value; } }
}
which did appear to work ok, but Envers did not work with it when retrieving audited data.
The only way that I can think of avoiding this issue is to map the property, but this is duplication since the base Teacher is already mapped to the Student hierarchy.
This is not duplication as you never mapped the SpecialStudent property in the SpecialTeacher mapping file. Although you correctly defined the relationship in code, NHibernate has no way of knowing a SpecialTeacher is suppose to have a SpecialStudent. The code is use by NHibernate to recreate the object from the tables, but only if you define the correct relationships in your mapping.
Remeber that BaseTeacher to BaseStudent does not imply SpecialTeacher to SpecialStudent relationship.

NHibernate multiple column mapping collection

I have this situation:
I have several tables table_1, table_2... table_n, they belong to different data but they have some fields in common, record_id, form_id where id is the primary key. All these tables are represented by a single class Record (Id, Form, Attributes)
I have another table tbl_attachments which have attachment_id, record_id, form_id (record_id is not enough because the record_id can be repeated over table_X tables.
The problem I have is I want to have property Attachments in class Record, to get the records attachments from tbl_attachments if any.
Can you help me with the mapping on nhibernate to get this done?
I appreciate any help.
Edit: Forgot to say that a Record on table_X can have multiple attachments :)
you can achieve this using AnyMapping
abstract class RecordBase
{
public virtual int Id { get; set; }
public virtual int FormId { get; set; }
public virtual ICollection<Attachment> Attachments { get; set; }
}
class RecordA : RecordBase
{ }
class Attachment
{
public virtual int id { get; set; }
public virtual RecordBase Record { get; set; }
}
class RecordAMap : ClassMap<RecordA>
{
public RecordAMap()
{
HasMany(x => x.Attachments)
.Where("Form_Id = 5");
}
}
class AttachmentMap : ClassMap<Attachment>
{
public AttachmentMap()
{
ReferencesAny(x => x.Record)
.EntityIdentifierColumn("record_id")
.EntityTypeColumn("form_id")
.IdentityType<int>()
.AddMetaValue<RecordA>("5");
}
}
UPDATE: xml mappings
<bag where="form_id=5">
<key column="record_id"/>
<one-to-many class="Attachment1"/>
</bag>
<any name="Item" id-type="System.Int32" meta-type="System.Int32">
<column name="form_id" />
<column name="record_id" />
</any>

NHibernate Automapping problem

Recently I came across a strange behavior in Automapping of Fluent NHibernate. I have the following class structure (some properties cut off for the sake of brewity).
public class UserGroup
{
public virtual UserGroup ParentGroup { get; set; }
public virtual UserGroupMember Manager { get; protected set; }
public virtual ISet<UserGroupMember> Members { get; protected set; }
}
and
public class UserGroupMember : BaseEntity
{
public virtual User User { get; set; }
public virtual UserGroup Group { get; set; }
}
The mapping for UserGroup:
public class UserGroupMap : IAutoMappingOverride<UserGroup>
{
public void Override(AutoMapping<UserGroup> mapping)
{
mapping.HasMany(el => el.Members)
.Cascade
.AllDeleteOrphan().Inverse().LazyLoad();
}
}
The automapping creates two column (both of which are foreign keys) in the UserGroupMember table to reflect the relation between UserGroup and UserGroupMembers. I've found out that the generated mapping contains wrong column (as seen below):
<set cascade="all-delete-orphan" inverse="true" lazy="true" name="Members" mutable="true">
<key>
<column name="Parent_Id" />
</key>
<one-to-many class="Groups.Data.UserGroupMember, Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>
which results in wrong queries:
While insert in UserGroupMember - Group_Id is used (which is right), not using Parent_Id
While select in UserGroupMember - Parent_Id is used
Group_Id is the column in UserGroupMember mapping file which reflects the Group property in UserGroupMember.
I tried to modify the mapping adding .KeyColumn("Group_Id") and it is solves the problem. But is there any way to make Fluent NHibernate 'think the right way'?
This is from memory, as I don't have test code ready.
When using bidirectional many-to-many, you sometimes have to help FHN figure columns names, if they're not "alike" on both sides.
For example this should map correcly
public class User
{
public IList<Group> Groups { get; set; }
}
public class Group
{
public IList<User> Users { get; set; }
}
While this would not
public class User
{
public IList<Group> BelongsTo { get; set; }
}
public class Group
{
public IList<User> Contains { get; set; }
}
As a rule of thumb, if automapping (with or without conventions) doesn't generate right columns names, especially for non trivial cases, do not hesitate to put an override to set those column names manually.

How to do this Unidirectional NHibernate one-to-one mapping?

This is a problem of unidirectional one-to-one mapping in NHibernate.
Student.cs
public class Student
{
public int ID { get; set; }
public int Roll { get; set; }
public int RegNo { get; set; }
public string Name { get; set; }
public StudentDetail StudentDetail { get; set; }
}
StudentDetail.cs
public class StudentDetail
{
public int ID { get; set; }
public string Father { get; set; }
public string Mother { get; set; }
}
How can I map these classes (how do the hbm mapping files look like) to the following case of one-to-one relationship?
Please have a look at the classes and the table very carefully.
Where can I put the <many-to-one> tag in Student.hbm.xml or StudentDetail.hbm.xml? If I put it in Student.hbm.xml, how can I map the column StudentDetail.StudentID, coz it is in a different table?
So this mapping:
<class name="Student" table="Student">
<id name="ID" column="ID">
<generator class="native"/>
</id>
.......
<many-to-one class="StudentDetail" name="StudentDetail" column="StudentID" unique="true" cascade="all" />
</class>
generates the following exception:
{"Invalid column name 'StudentID'."}
On the other hand <many-to-one> can't be placed in StudentDetail.hbm.xml. Coz, StudentDetail.cs doesn't contain any property of type Student.
Can I use <one-to-one>-tag? If yes where should I place it, in Student.cs or StudentDetail.cs? And how should I configure it?
Case #1:
In Student...
<one-to-one name="StudentDetail"
cascade="save-update,delete"
property-ref="Student" />
In StudentDetail...
<many-to-one name="Student"
column="StudentID"
unique="true"
cascade="none" />
Note that you'll have to have a property in your StudentDetail class that refers to a Student oobject (called Student). Also, your cascades might be different depending on your usage. You most likely want the delete cascade in there, though.
The unique="true" ensures the one-to-one mapping on the StudentDetail side.
Case #2:
Just exchange the two mappings, making sure you change the property names to the opposite class.
Look here for more info:
http://nhforge.org/blogs/nhibernate/archive/2009/04/19/nhibernate-mapping-lt-one-to-one-gt.aspx
You can map it as a one-to-many, with the collection property hidden and only its first element publicly exposed:
public class Student
{
public virtual int ID { get; set; }
public virtual int Roll { get; set; }
public virtual int RegNo { get; set; }
public virtual string Name { get; set; }
protected virtual IList<StudentDetail> StudentDetails { get; set; }
public virtual StudentDetail StudentDetail
{
get
{
if (StudentDetails.Count == 0) return null;
return StudentDetails[0];
}
set
{
if (StudentDetails.Count != 0) throw new Exception();
StudentDetails.Add(value);
value.Student = this;
}
}
}
You could handle the setter better than this - the point is to make sure you don't add multiple rows to the one-to-many. Obviously in this, StudentDetails is mapped but StudentDetail isn't in your .hbm.xml or Fluent mappings.

Fluent NHibernate - Map 2 tables to one class

I have a table structure something like this
table Employees
EmployeeID
EmployeeLogin
EmployeeCustID
table Customers
CustomerID
CustomerName
What i would like is to map the structure above to one single class named:
Class Employee
EmployeeID
EmployeeLogin
EmployeeName
How do i do that with fluent nhibernate ?
I don't know if it is possible with fluent, but in xml you use the join element:
simplified:
<class name="Employee" table="Customers" >
<id name="CustomerID" .../>
<property name="CustomerName"/>
<join table="Employees">
<key column="EmployeeCustID" />
<property name="EmployeeLogin" />
</join>
</class>
See this post by Ayende
I agree with Frans above but if you're stuck with someone else's code and have to use the existing structure, you can can use WithTable.
public class EmployeesMap : ClassMap<Employees>
{
public EmployeesMap()
{
Id(x => x.EmployeeId);
Map(x => x.EmployeeLogin);
WithTable("Customers", join =>
{
join.Map(m => m.EmployeeName, "CustomerName");
join.WithKeyColumn("EmployeeCustID");
});
}
}
[DataContract(IsReference = true)]
public class Employees
{
[DataMember]
public virtual int EmployeeId { get; set; }
[DataMember]
public virtual string EmployeeLogin { get; set; }
[DataMember]
public virtual string EmployeeName { get; set; }
}
I have not tried this since Fluent NHibernate went to 1.0 so my syntax may be incorrect. I'm pretty sure this will only work if Customer.CustomerId is a foreign key to Employee.
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.EmployeeId);
Map(x => x.EmployeeLogin);
Table("Customer", m =>
{
m.Map(x => x.EmployeeName, "CustomerName");
});
}
}
Is EmployeeCustID unique? If not, this is never going to work, as you then try to cram two different entity types into 1. Also, with your structure, how do you want to save an instance? -> the CustomerID isn't known, so you can't save such an entity.
IMHO it's better to simply keep Customer as a related entity to Employee, as (I assume) the EmployeeCustID is used to link a Customer entity to an Employee entity if the employee is also a customer, which means 'customer' is just a role for employee and therefore optional and changeable and thus should be a separate entity.