Fluent NHibernate Many to One Mapping Problem - fluent-nhibernate

Edit
I have the answer and will post it up tomorrow.
I have a class that appears to be using the parent primary key column instead of it's own primary key column.
There are two classes involved here ActionHistory and ActionData. The parent class, ActionHistory, represents an action and details who performed the action. The child class, ActionData, represents a piece of data that was created by the action. Each ActionHistroy has a number of ActionData and this is represented through a many to one mapping.
The classes are:
public class ActionHistory
{
public virtual int Code { get; set; }
public virtual string Doneby { get; set; }
public virtual IList<ActionData> _ActionData{ get; set;}
}
public class ActionData
{
public virtual int Code { get; set; }
public virtual double Data{ get; set; }
public virtual ActionHistory _ActionHistory{ get; set; }
}
My mappings are:
public class ActionHistoryClassMap : ClassMap<ActionHistory>
{
public ActionHistoryClassMap ()
{
Table("ACTIONHISTORY");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Doneby, "DONEBY");
HasMany(x => x._Actiondata).KeyColumn("CODE").AsBag().Cascade.All();
}
}
public class ActionDataClassMap : ClassMap<ActionData>
{
public ActionDataClassMap ()
{
Table("ACTIONDATA");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Data, "DATA");
References(x => x._ActionHistory, "ACTIONHISTORYID");
}
}
The problem is when an ActionHistory class is queried the correct ActionHistory is returned but the HistoryData that it contains is wrong. A list of HistoryData should be returned but only one HistoryData is returned and it's Code value is the Code values of the parent ActionHistory class. I believe my problem stems form the fact that both classes have the same primary key column name and I'm not handling this properly. The columns that that are related are CODE from the ACTIONHISTORY table and ACTIONHISTORYID from the ACTIONDATA table. The database is fixed so I can't rename any columns. In this case data is only read from the database and not saved or updated.
I downloaded NHibernate Profiler to help me with this problem and have found the exact point in the SQL where the problem occurs.
This is the SQL query that is generated to return the ActionData (where 18 is the ActionHistory primary key):
SELECT actiondat0_.CODE as CODE1_,
actiondat0_.CODE as CODE9_0_,
actiondat0_.DATA as DATA9_0_,
actiondat0_.ACTIONHISTORYID as ACTIONHI4_9_0_
FROM ACTIONDATA actiondat0_
WHERE actiondat0_.CODE = 18 /* #p0 */
The last line should be:
WHERE actiondat0_.ACTIONHISTORYID = 18 /* #p0 */
But I just do not understand why the mapping is not generating the right query.

I know it was something blindingly obvious and after hours of getting nowhere it of course jumps out at me.
The ActionHistory class map should of course be:
public class ActionHistoryClassMap : ClassMap<ActionHistory>
{
public ActionHistoryClassMap ()
{
Table("ACTIONHISTORY");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Doneby, "DONEBY");
HasMany(x => x._Actiondata).KeyColumn("ACTIONHISTORYID")
.AsBag().Cascade.All();
}
}
The difference being the KeyColumn method is now ACTIONHISTORYID as opposed to CODE. I didn't cop that KeyColumn should reference the ActionHistory key column. It seems obvious now and it didn't help that all other times I implemented it in this project the columns with the relationship always had the same name.

Related

NHibernate Save Is Trying to Clear Child KeyColumn Id On Update

I am trying to create a parent object that has multiple children in a 1 to many relationship. I am not referencing the Parent object on the child object, instead I am mapping its keycolumn as a field.
When I try to save this object for the first time, it works as expected without any issues (cascading all the Id's and children). When I try to get the object from the database, update some properties on it and re-save it again, it fails. The actual error message I am getting is "Could not delete collection".
The error above is due to the fact that it is trying to set the "ParentId" field on the child objects to NULL (which violates the FK constraint I have in the db). If I remove this constraint from the DB, the end result is what I want; however, I do not want it to perform this update (setting parent id to null) at all and I'm not sure why it is. From what I can tell in the SQL code it is generating and sending to the DB, everything else appears to be correct and it would all work if it wasnt for that last update statement.
Obviously, I must have something wrong with my mapping but I cannot figure out what. I tried adding Not.KeyUpdate() but that simply made it not generate a key at all. Does anyone have any ideas what I am doing wrong??
Thanks in advance, I really appreciate it!!!
Please see below for my mapping:
public class Parent
{
public Parent()
{
Children = new List<Child>();
}
public virtual Guid Id { get; set; }
public virtual IList<Child> Children { get; set; }
}
public class Child
{
public virtual Guid Id { get; set; }
public virtual Guid ParentId { get; set; }
}
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Table("Parent");
Id(x => x.Id);
HasMany(x => x.Children).KeyColumn("ParentId").Cascade.SaveUpdate().Not.LazyLoad();
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Table("Child");
Id(x => x.Id);
Map(x => x.ParentId);
}
}
This is caused by the fact, that the Collection of children is not marked as inverse="true".
What you can do is: I. to remove the constraint from DB. NHiberante simply must (without the inverse setting) do 2 steps. Firstly update record to break relation, secondly (due to cascades) alse delete the item
II. Change the mapping, and entities. Like this:
A Child must have reference to the Parent:
public class Child
{
public virtual Guid Id { get; set; }
public virtual Guid ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Mapping then will be like this:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Table("Parent");
Id(x => x.Id);
HasMany(x => x.Children)
.KeyColumn("ParentId")
.Cascade.SaveUpdate()
.Not.LazyLoad()
.Inverse() // here we do have the inverse setting
;
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Table("Child");
Id(x => x.Id);
Map(x => x.ParentId).Not.Insert().Not.Update(); // this is readonly now
References(x => x.Parent).Column("ParentId"); // just a Parent is writable
}
}
Now, you have to all the time properly set the relation in the C# code. I.e. if child is added to Parents collection, it should also get set the Parent reference
parent.Children.Add(child);
child.Parent = parent;
NHibernate will now issue only one statement, to delete child from its table

Having trouble deleting the child objects in a one-many relationship using NHibernate

The title pretty much sums up the issue that I am facing. Basically I have 2 classes:
public class Parent : IIntegerIdentifiable
{
public virtual long Id { get; set; }
public virtual ICollection<Child> Children {get; set; }
}
public class Child : IIntegerIdentifiable
{
public virtual long Id { get; set; }
public virtual Parent Parent { get; set; }
}
I'm defining a one-to-many relationship between these classes.The class maps for both of them are:
public sealed class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Id(x => x.Id).GeneratedBy.HiLo(
"HiLoUniqueKey", "NextHi", "99", "ObjectType = 'Parent'");
HasMany(x => x.Children).KeyColumn("Parent_Id").Inverse().Cascade.AllDeleteOrphan();
}
}
public sealed class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Id(x => x.Id).GeneratedBy.HiLo("HiLoUniqueKey", "NextHi", "99", "ObjectType = 'Child'");
References(x => x.Event).Cascade.All();
}
}
Since I wanted to delete All the children for a particular parent from the database, I wrote the following function in ChildRepository:
public void ClearChildEntries(long parentId)
{
//I had tried the following and was getting errors before I read the other posts on the topic
//QueryOver().Where(x => x.Event.Id == eventId).List().ToList().ForEach(Remove);
//Session.flush()
//Current code
var parent = _parentRepository.GetById(parentId);
parent.Children.Clear();
_parentRepository.SaveOrUpdate(parent);
}
Our code is setup so that the flush gets called at end of request. Whenever I try executing the code, I get the error:
Cannot insert the value NULL into column 'parent_id', table 'test.dbo.Child'; column does not allow nulls. UPDATE fails.
I also tried first removing each of the child entities and flushing the session and then updating the parent collection. It gives the same error. The question is
a) Why is nHibernate trying to update the parent id column to null and then delete?
b) What can I do to get the deletes working?
Just so that you know I'm not wasting your time, I have referred other posts on the topic, like
- How to delete child object in NHibernate?
- http://ayende.com/blog/1890/nhibernate-cascades-the-different-between-all-all-delete-orphans-and-save-update
but nothing seems to work. Can someone please point me in the right direction? Thank you for your help!!
I think it has to do something with the Cascade option try changing the Cascade to All_Delete_Orphan From Cascade.All
http://ayende.com/blog/1890/nhibernate-cascades-the-different-between-all-all-delete-orphans-and-save-update

Fluent NHibernate Relationship Mapping and Save Exception

I'm new to NHibernate and am attempting to use Fluent's AutoMapping capability so that I do not need to maintain separate XML files by hand. Unfortunately I'm running into a problem with referenced entities, specifically 'Exception occurred getter of Fluent_NHibernate_Demo.Domain.Name.Id' - System.Reflection.TargetException: Object does not match target type.
I appear to have an error in at least one of my mapping classes although they do generate the correct SQL (i.e. the created tables have the correct indexes).
The implementations for my domain models and mappings are:
Name.cs
public class Name
{
public virtual int Id { get; protected set; }
public virtual string First { get; set; }
public virtual string Middle { get; set; }
public virtual string Last { get; set; }
}
Person.cs
public class Person
{
public virtual int Id { get; protected set; }
public virtual Name Name { get; set; }
public virtual short Age { get; set; }
}
NameMap.cs
public NameMap()
{
Table("`Name`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
Map(x => x.First).Column("`First`").Not.Nullable().Length(20);
Map(x => x.Middle).Column("`Middle`").Nullable().Length(20);
Map(x => x.Last).Column("`Last`").Not.Nullable().Length(20);
}
PersonMap.cs
public PersonMap()
{
Table("`Person`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
References<Name>(x => x.Name.Id, "`NameId`").Not.Nullable();
// There's no exception if the following line is used instead of References
// although no constraint is created
// Map(x => x.Name.Id).Column("`NameId`").Not.Nullable();
Map(x => x.Age).Column("`Age`").Nullable();
}
Finally, the following code will produce the exception:
Name name = new Name { First = "John", Last = "Doe" };
session.Save(name);
Person person = new Person { Name = name, Age = 22 };
session.Save(person); // this line throws the exception
As mentioned, the created schema is correct but I'm unable to save using the above code. What is the correct way to create a foreign key constraint using Fluent NHibernate?
If you want to reference the name, by ID, then that's what you should do. NHibernate is smart enough to figure out what the actual FK field on Person should be and where it should point; that is, after all, the job an ORM is designed to perform.
Try this mapping:
public PersonMap()
{
Table("`Person`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
References(x => x.Name, "`NameId`").Not.Nullable();
Map(x => x.Age).Column("`Age`").Nullable();
}
You've mapped the person and the name; as a result, NHibernate knows which property is the ID property of Name, and can create and traverse the foreign key on Person.

Fluent NHibernate Inheritance Issue

I'm using Nhibernate with Fluent and I encounter an issue with inheritance.
Here my DB schema
TABLE Base
IDBASE (PK)
Field1
TYPE
TABLE T1
IDBASE (PK and FK)
Field2
Field3 ...
My mapping files are :
public class BaseMap: ClassMap<BASE>
{
public BaseMap()
{
Id(x => x.Id, "IDBASE").GeneratedBy.Identity();
Map(x => x.Field1);
DiscriminateSubClassesOnColumn("TYPE");
}
}
public class T1Map: SubclassMap<T1>
{
public T1Map()
{
Table("T1");
KeyColumn("IDBASE");
DiscriminatorValue("T1");
Map(x => x.Field2).Not.Nullable();
Map(x => x.Field3).Not.Nullable();
}
}
I use FluentMappings instead of AutoMapping.
Here my entities :
public abstract class BASE
{
public virtual long IdBase{ get; set; }
public virtual string Field1 { get; set; }
}
public class T1: BASE
{
public virtual string Field2 { get; set; }
public virtual string Field3 { get; set; }
}
T1 entity inherits from BASE entity, the issue is when I try to get a row NHibernate try to select Field2 and Field3 on the Base Table whereas they should be selected on T1 Table.
I've tried dozens of hacks but it still doesn't work, if anyone as an idea it would be very helpful.
Thanks a lot.
You're specifying a discriminator, that implies that the inheritance structure should be a table-per-class-hierarchy; you won't have two tables with this setup, you'll have a single table with everything in (hence why the select is hitting the same table for all the columns).
If you remove the DiscriminateSubclassesOnColumn call, that'll put your mappings into a table-per-class, so you'll have your desired structure.
Actually I really need the DiscriminateSubclassesOnColumn so I found an other solution, in my inherited entity I do the mapping with the join like this :
public class T1Map: SubclassMap<T1>
{
public T1Map()
{
Join("T1", y =>
{
y.KeyColumn("IDBASE");
y.Map(x => x.Field2).Not.Nullable();
y.Map(x => x.Field3).Not.Nullable();
});
}
}
And it works fine, I can keep the Discriminator column on my base class.
Thanks

Fluent NHibernate: How to create one-to-many bidirectional mapping?

Basic question: How to I create a bidirectional one-to-many map in Fluent NHibernate?
Details:
I have a parent object with many children. In my case, it is meaningless for the child to not have a parent, so in the database, I would like the foreign key to the parent to have NOT NULL constraint. I am auto-generating my database from the Fluent NHibernate mapping.
I have a parent with many child objects like so:
public class Summary
{
public int id {get; protected set;}
public IList<Detail> Details {get; protected set;}
}
public class Detail
{
public int id {get; protected set;}
public string ItemName {get; set;}
/* public Summary Owner {get; protected set;} */ //I think this might be needed for bidirectional mapping?
}
Here is the mapping I started with:
public class SummaryMap : ClassMap<Summary>
{
public SummaryMap()
{
Id(x => x.ID);
HasMany<Detail>(x => x.Details);
}
}
public class DetailMap : ClassMap<Detail>
{
public DetailMap()
{
Id(x => x.ID);
Map(x => x.ItemName).CanNotBeNull();
}
}
In the Detail table, the Summary_id should be Not Null, because in my
case it is meaningless to have a Detail object not attached to the
summary object. However, just using the HasMany() map leaves the Summary_id foreign key nullable.
I found in the NHibernate docs (http://www.hibernate.org/hib_docs/nhibernate/html/collections.html) that "If the parent is required, use a bidirectional one-to-many association".
So how do I create the bidirectional one-to-many map in Fluent NHibernate?
To get a bidirectional association with a not-null foreign key column in the Details table you can add the suggested Owner property, a References(...).CanNotBeNull() mapping in the DetailsMap class, and make the Summary end inverse.
To avoid having two different foreign key columns for the two association directions, you can either specify the column names manually or name the properties in a way that gives the same column name for both directions. In this case you I suggest renaming the Details.Owner property to Details.Summary.
I made the Summary id generated by increment to avoid problems when inserting into the table since Summary currenty has no columns besides id.
Domain:
public class Detail
{
public int id { get; protected set; }
public string ItemName { get; set; }
// Renamed to use same column name as specified in the mapping of Summary.Details
public Summary Summary {get; set;}
}
public class Summary
{
public Summary()
{
Details = new List<Detail>();
}
public int id { get; protected set; }
public IList<Detail> Details { get; protected set; }
}
Mapping:
public class DetailMap : ClassMap<Detail>
{
public DetailMap()
{
Id(x => x.id)
.GeneratedBy.Native();
Map(x => x.ItemName)
.CanNotBeNull();
References<Summary>(x => x.Summary)
// If you don't want to rename the property in Summary,
// you can do this instead:
// .TheColumnNameIs("Summary_id")
.CanNotBeNull();
}
}
public class SummaryMap : ClassMap<Summary>
{
public SummaryMap()
{
Id(x => x.id)
.GeneratedBy.Increment();
HasMany<Detail>(x => x.Details)
.IsInverse()
.AsBag(); // Use bag instead of list to avoid index updating issues
}
}