NHibernate multiple column mapping collection - nhibernate

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>

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.

Insert in two related table at once with Nhibernate

I have two entities like
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public int DataId { get; set; }
}
public class Data
{
public int DataId { get; set; }
public string details { get; set; }
public int PersnId{ get; set; }
}
as you see both table are relate to each other. I want a solution to insert data in both table at once. I 1-insert person, 2-insert data and then update person and it works but I'm looking for way to eliminate Update.
My mapping for person table:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="TestNhibrinate" assembly="TestNhibrinate">
<class name="TestNhibrinate.Entites.Person" table="Person" lazy="false">
<id name="PersonId" column="PersonId" type="int" >
<generator class="identity" />
</id>
<property name="Name" column="Name" type="String" length="50" />
<many-to-one name="Adress" class="TestNhibrinate.Entites.Adress" column="AdressId"/>
</class>
</hibernate-mapping>
and same mapping for data.
You entities should look like this:
public class Person
{
public Person()
{
DataCollection = new List<Data>();
}
public int PersonId { get; set; }
public string Name { get; set; }
public int DataId { get; set; }
public IList<Data> DataCollection{get;set;}
public void AddData(Data item)
{
if(!DataCollection.Contains(item))
{
DataCollection.Add(item);
}
}
}
public class Data
{
public int DataId { get; set; }
public string details { get; set; }
public Person Person{ get; set; }
}
This way you create a one-to-many relation from Person to Data. If you save your person entity after you added some data, the data will also be persisted. This depends on your cascade options offcourse.
I'm not sure how to map this with XML mappings, since i always use Fluent or Auto mappings.

nhibernate mapping, many to one

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.

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.