map table to itself in Parent-Child relationship Fluent Nhibernate - nhibernate

I have situation where I am mapping table's columns to the primary key of same table. The table looks like this
---+-------+---------
ID Name ParentId
---+-------+---------
1 Parent1 0
2 Child 1 1
3 Child 2 1
4 Parent2 0
5 Child 3 4
I have created a following Model and Fluent NHibernate mapping class
//Model.LocationType.cs
public class LocationType
{
public virtual long Id { get; set; }
public virtual string ShortName { get; set; }
public virtual string Description { get; set; }
public virtual IList<LocationType> ParentId { get; set; }
}
and
//Mapping.LocationTypeMap.cs
public class LocationTypeMap : ClassMap<LocationType>
{
public LocationTypeMap()
{
Table("SET_LOC_TYPE");
Id(x => x.Id).Column("LOC_TYPE_ID").GeneratedBy.Assigned();
Map(x => x.ShortName, "SHORT_NAME").Length(15).Not.Nullable();
Map(x => x.Description, "LOC_DESC").Length(50).Not.Nullable();
References(x => x.ParentId).Column("PARENT_LOC_TYPE_ID").Cascade.SaveUpdate();
}
}
but I am receiving follow error message when i execute my code:
Unable to cast object of type 'SmartHRMS.Core.Domain.Model.LocationType' to type 'System.Collections.Generic.IList`1[SmartHRMS.Core.Domain.Model.LocationType]'.
Edit 1:
Instead of using i tried
HasMany(x => x.ParentIds).KeyColumn("PARENT_LOC_TYPE_ID");
Although it worked and solved the casting problem that I mentioned above but the result I am gettting is the reverse of what i need.
In parent's LocationType objects, it lists all childs in IList, so for above example the result will be:
-----+----------+------
ID Name ParentId
-----+----------+------
1 Parent1 IList<Child1, Child2>
2 Child 2 IList<Empty>
3 .... same
4 Parent2 IList<Child3>
5 Child 3 IList<Empty>

Seems like you should use HasMany in your mapping instead of References.

Related

Fluent NHibernate HASMANY mapping without references

I am a beginner at using Fluent NHibernate.
I am developing a C# application that has to interact with an existing database.Let say I have 2 tables: Items and ItemsList.
Items: ID INT ItemName VARCHAR(100)
ItemsList: ID INT ChildItemID INT
I've built 2 classes and their mapping:
public class Items
{
public virtual int id {get; set;}
public virtual string itemName {get; set;}
}
public class ItemsMap : ClassMap<Items>
{
public ItemsMap()
{
Id(x => x.id).GeneratedBy.Increment();
Map(x => x.itemsName);
}
}
public class ItemsList()
{
public virtual int id {get; set;}
public virtual IList<Items> childItems {get; set;}
public ItemsList()
{
childItems = new List<Items>();
}
}
public class ItemsListMap : ClassMap<ItemsList>
{
public ItemsListMap()
{
Id(x => x.id).GeneratedBy.Increment();
HasMany(x => x.childItems).KeyColumn("childID").Cascade.All();
}
}
And finally, I insert an item in the itemsList and save it all:
try
{
using( ISession session = NH.OpenSession())
{
using(ITransaction transaction = session.BeginTransaction())
{
Items i = New Items()
i = session.get<Items>(1);
ItemsList il = new ItemsList();
il.childID.Add(i);
session.SaveOrUpdate(il);
transaction.Commit();
}
}
}
So when I commit, I have a new entry in ItemsList table, but the childID is blank.
Question:
All the examples I see has a reference to ItemsListID in Items table. But I don't want to have this reference since I want the item to be unique in the items table. How can I acheve that?
The NHibernate native way for expressing the unique reference, is:
5.1.12. one-to-one
There are two varieties of one-to-one association:
primary key associations
unique foreign key associations
Primary key associations don't need an extra table column; if two rows are related by the association then the two table rows share the same primary key value. So if you want two objects to be related by a primary key association, you must make sure that they are assigned the same identifier value!...
Other words, Tables would look like this (Table Items generates the value of ItemID, table ItemsList takes that value and stores it in the ItemID ) :
Items: ItemID INT ItemName VARCHAR(100)
ItemsList: ItemID INT
The C# would be (I changed Items into Item and ItemList into ItemMoreDetails, because it is not a list anymore)
public class Item
{
public virtual int ItemId { get; set; }
...
public virtual ItemMoreDetails ItemMoreDetails {get; set; }
public class ItemMoreDetails
{
public virtual int ItemId { get; set; }
...
public virtual Item Item {get; set;}
The mapping would be (in fluent):
// Parent side
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(x => x.id).GeneratedBy.Increment();
...
HasOne(x => x.ItemMoreDetails).Cascade.All();
// child side
public class ItemMoreDetailsMap: ClassMap<ItemMoreDetails>
{
public ItemMoreDetailsMap()
{
...
References(x => x.parent).Unique();
See the doc:
HasOne / one-to-one

(Fluent) NHibernate: Map field from separate table into object

I'm currently trying to get (Fluent)NHibernate to map an object to our legacy database schema. There are three tables involved.
Table a contains most information of the actual object I need to retrieve
Table b is a table which connects table a with table c
Table c has one additional field I need for the object
An SQL query to retrieve the information looks like this:
SELECT z.ID, z.ZANR, e.TDTEXT
FROM PUB.table_a z
JOIN PUB.table_b t ON (t.TDKEY = 602)
JOIN PUB.table_c e ON (e.ID = t.ID AND e.TDNR = z.ZANR)
WHERE z.ZANR = 1;
The main problem is how to specify these two join conditions in the mapping.
Entity for table a looks like this:
public class EntityA
{
public virtual long Id { get; set; }
public virtual int Number { get; set; }
public virtual string Name { get; set; }
}
Name should map to the column table_c.TDTEXT.
The mapping I have so far is this:
public class EntityAMap : ClassMap<EntityA>
{
public EntityAMap()
{
Table("PUB.table_a");
Id(x => x.Id).Column("ID");
Map(x => x.Number).Column("ZANR");
}
}
I tried to map the first join with the same strategy as in How to join table in fluent nhibernate, however this will not work, because I do not have a direct reference from table_a to table_b, the only thing connecting them is the constant number 602 (see SQL-query above).
I didn't find a way to specify that constant in the mapping somehow.
you could map the name as readonly property easily
public EntityAMap()
{
Table("PUB.table_a");
Id(x => x.Id).Column("ID");
// if ZANR always has to be 1
Where("ZANR = 1");
Map(x => x.Number).Column("ZANR");
Map(x => x.Name).Formula("(SELECT c.TDTEXT FROM PUB.table_b b JOIN PUB.table_c c ON (c.ID = b.ID AND b.TDKEY = 602 AND c.TDNR = ZANR))");
}
Update: i could only imagine a hidden reference and the name property delegating to there
public EntityAMap()
{
HasOne(Reveal.Member<EntityA>("hiddenEntityC"));
}
public class EntityB
{
public virtual int Id { get; set; }
public virtual EntityA EntityA { get; set; }
}
public EntityCMap()
{
Where("Id = (SELECT b.Id FROM PUB.table_b b WHERE b.TDKEY = 602)");
References(x => x.EntityA).PropertyRef(x => x.Number);
}

Mapping One to One in FluentNHibernate, NHibernate tries to write child before parent

I'm trying to do a 1:1 relationship in FN but its being a bit of a pain.
I looked at http://avinashsing.sunkur.com/2011/09/29/how-to-do-a-one-to-one-mapping-in-fluent-nhibernate/ which seemed to confirm this should work but hey ho, it keeps trying to insert records into the child tables before the parent which means the child-inserts dont contain the CustomerId which is required as its a foreign key constraint.
Tables
+-----------------------+ +----------------------+
| tblCustomer | | tblCustomerPhoto |
|-----------------------| |----------------------|
| | 1:1 | |
| CustomerID (PK) |+---->| CustomerID (FK) |
| OtherJunk... | | Photo (Image) |
| | | |
| | | |
+-----------------------+ +----------------------+
Models
public class Customer
{
public virtual int CustomerID { get; private set; }
/* public virtual Other stuff */
}
public class CustomerPhoto
{
public virtual int CustomerID { get;set;}
public virtual Byte[] Photograph { get; set; }
}
Maps
public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
public CustomerPhotoMap()
{
Id(x => x.CustomerID)
.Column("CustomerID")
.GeneratedBy.Assigned();
Map(x => x.Photograph)
.CustomSqlType("Image")
.CustomType<Byte[]>()
.Nullable();
}
}
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID)
.GeneratedBy.Identity()
.Column("CustomerID");
HasOne<CustomerPhoto>(x => x.CustomerPhoto)
.LazyLoad()
.ForeignKey("CustomerID");
}
}
Test
class CustomerMappingFixture : IntegrationTestBase
{
[Test]
public void CanMapCustomer()
{
new PersistenceSpecification<Customer>(Session)
.CheckReference(x => x.CustomerPhoto, new CustomerPhoto(){ Photograph = Arrange.GetBitmapData(ImageFormat.Jpeg) })
.VerifyTheMappings();
}
}
Now the foreign key column on the CustomerPhoto was set to not-null and I was repeating this in the notation on the CustomerPhotoMapping.
On the basis of ( https://stackoverflow.com/a/2286491/529120 ) I changed that to nullable and removed the notation from the mapping.
Regardless of which, NHibernate returns
System.NullReferenceException : Object reference not set to an instance of an object.
and appears to be trying to insert a CustomerPhoto record first, passing zero as the CustomerId; then creating the Customer record, then trying to select the customer and photo using a left outer join. Which obviously wont work as at no point has it attempted to update the ID in the photo table.
Few things I noticed
Using CheckReference to verify this mapping is probably incorrect. I'm pretty sure this is only for many-to-one relationships. Therefore it makes sense that it's trying to insert the CustomerPhoto before the Customer. I would write a test using straight up NH sessions here. I found it to be more trouble than it's worth to use PersistenceSpecification for many of my mappings that were non-trivial.
The one to one mappings looked like they were a bit off (proposed solution below)
When mapping an image column to a byte array I don't think there is a need to declare a custom type. The mapping below has worked fine for me. I think the other stuff you have on that property mapping is un-needed.
I have a mapping almost identical to yours and this is what I use:
public class Customer
{
public virtual int CustomerID { get; private set; }
public virtual CustomerPhoto CustomerPhoto { get; set; }
/* public virtual Other crap */
}
public class CustomerPhoto
{
public virtual Customer Customer { get; set; }
public virtual Byte[] Photograph { get; set; }
}
public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
public CustomerPhotoMap()
{
Id(x => x.Id)
.Column("CustomerID")
.GeneratedBy.Foreign("Customer");
Map(x => x.Photograph).Length(Int32.MaxValue);
}
}
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID).GeneratedBy.Identity()
.Column("CustomerID");
HasOne(x => x.CustomerPhoto)
.Cascade.All();
}
}

NHibernate returning duplicate rows

NHibernate appears to be returning the contents of the first row multiple times. As many times as there are actual, distinct rows in the database. For example, if one person has 3 campus affiliations like this:
Baker College - Teacher
Bryant Elementary - Teacher
Ohio State University - Student
NHibernate will return it like this:
Baker College - Teacher
Baker College - Teacher
Baker College - Teacher
I'm using FluentNHibernate. Here are some snippets of the entity and mapping files:
public class Person
{
public virtual string SysID { get; set; }
public virtual string FullName { get; set; }
public virtual ICollection<Campus> Campuses { get; set; }
}
public class Campus
{
public virtual string SysID { get; set; }
public virtual string Name { get; set; }
public virtual string Affiliation { get; set; }
}
public class PersonMapping
{
Table("Person");
Id(x => x.SysId);
Map(x => x.FullName).Column("FULL_NAME");
HasMany(x => x.Campuses).KeyColumn("SysId");
}
public class CampusMapping
{
Table("Campus");
Id(x => x.SysID);
Map(x => x.Name);
Map(x => x.Affiliation);
}
I'm iterating through the campuses in my view (MVC 3) like this:
#foreach(var campus in Model.Campuses)
{
#campus.Name #campus.Affiliation
}
I also tried adding this to the entity to make sure it wasn't a silly mistake with MVC or Razor and had the same result:
public virtual string campusesToString
{
get
{
string s = "";
for (int i = 0; i < Campuses.Count; i++)
{
s = s + Campuses.ElementAt(i).Name + " ";
}
return s;
}
}
Finally, I checked the SQL that was being output, and it's correct, and it's returning all of the rows uniquely...
Your mapping looks a bit weird.
First you set up this relationship: Person 1 -> * Campus
But in your mapping of Campus you make SysId primary key, but it is also the foreign key to Person? I think that is what confuses NHibernate..
What I think happens is that NHibernate sees the same SysId key multiple times and since it will resolve the same object for the same primary key to preserve object indentity it will return the same Campus object multiple times even though the other columns have different data.
You might want to use a many-to-many mapping as otherwise each Campus will only be able to have one person which seems wrong.
Ok. I found out my problem. I was indicating the Id in the mapping incorrectly.
There is one person and multiple campuses. The person ID is called SysID. It is also the foreign key on campus. But SysID is not the unique ID for campus. NHibernate was confused because it was trying to use the person unique ID for the campus ID.
public class Campus
{
public virtual string CampusID { get; set; }
public virtual string SysID { get; set; }
public virtual string Name { get; set; }
public virtual string Affiliation { get; set; }
}
public class CampusMapping
{
Table("Campus");
Id(x => x.CampusID);
Map(x => x.SysID);
Map(x => x.Name);
Map(x => x.Affiliation);
}

Nhibernate ManyToMany with a dynamic where clause

I'm having some issues mapping a complex many to many relationship in fluentnhibernate. I have a legacy db which looks something like this:
Foos: | Id | Foo |
FooBars: | FooId | BarId |
Bars: | Id | Bar | CultureId |
which I am trying to map to the following object model:
class Foo
{
property virtual int Id { get; set; }
property virtual string Foo { get; set; }
property virtual IList<Bar> Bars { get; set; }
}
class Bar
{
property virtual int Id { get; set; }
property virtual int CultureId { get; set; }
}
with the mappings:
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("foos");
Id(v => v.Id);
Map(v => v.Foo);
HasManyToMany(v => v.Bars)
.Table("FooBars")
.ParentKeyColumn("FooId")
.ChildKeyColumn("BarId")
.Cascade.All();
}
}
public class BarMapping : ClassMap<Bar>
{
public BarMapping()
{
Table("bars");
Id(v => v.Id);
Map(v => v.Bar);
Map(v => v.CultureId);
}
}
The problem is I have multiple Bar's with the same Id for different CultureIds
e.g.
I would have a table that looks like:
Id|Bar|CultureId
1, Hello, 1
1, Bonjour, 2
1, Gutentag, 3
At the moment, the Bars property for the above table will return 3 elements but the Bar property on it will return "Hello" for all three elements (presumably because they all have the same identity). So my question is, how can I either stop this happening or can anyone suggest a way of filtering rows that do not have the correct culture id (note, this is dynamic & based on the current culture)?
You can't create dynamic where clauses in your mappings. You're going to need to query this collection instead of accessing it via the parent, using a Criteria or HQL query. You could read up on filters, but they still require a query.