Fluent NHibernate one-to-many relationship setting foreign key to null - nhibernate

I have a simple Fluent NHibernate model with two related classes:
public class Applicant
{
public Applicant()
{
Tags = new List<Tag>();
}
public virtual int Id { get; set; }
//other fields removed for sake of example
public virtual IList<Tag> Tags { get; protected set; }
public virtual void AddTag(Tag tag)
{
tag.Applicant = this;
Tags.Add(tag);
}
}
public class Tag
{
public virtual int Id { get; protected set; }
public virtual string TagName { get; set; }
public virtual Applicant Applicant { get; set; }
}
My fluent mapping is the following:
public class ApplicantMap : ClassMap<Applicant>
{
public ApplicantMap()
{
Id(x => x.Id);
HasMany(x => x.Tags).Cascade.All();
}
}
public class TagMap : ClassMap<Tag>
{
public TagMap()
{
Id(x => x.Id);
Map(x => x.TagName);
References(x => x.Applicant).Not.Nullable();
}
}
Whenever I try to update an applicant (inserting a new one works fine), it fails and I see the following SQL exception in the logs:
11:50:52.695 [6] DEBUG NHibernate.SQL - UPDATE [Tag] SET Applicant_id = null WHERE Applicant_id = #p0;#p0 = 37 [Type: Int32 (0)]
11:50:52.699 [6] ERROR NHibernate.AdoNet.AbstractBatcher - Could not execute command: UPDATE [Tag] SET Applicant_id = null WHERE Applicant_id = #p0 System.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Applicant_id', table 'RecruitmentApp.dbo.Tag'; column does not allow nulls. UPDATE fails.
Why is NHibernate trying to update the tag table and set Applicant_id to null? I'm at a loss on this one.

Set Applicant.Tags to Inverse will instruct NHibernate to save Tags after the Applicant.
public class ApplicantMap : ClassMap<Applicant>
{
public ApplicantMap()
{
Id(x => x.Id);
HasMany(x => x.Tags).Cascade.All().Inverse();
}
}
More detail:
Inverse (as opposed to .Not.Inverse()) means the other side of the relationship (in this case, each Tag) is responsible for maintaining the relationship. Therefore, NHibernate knows that the Applicant must be saved first so that Tag has a valid foreign key for its Applicant.
Rule of thumb: The entity containing the foreign key is usually the owner, so the other table should have Inverse

Related

NHibernate - Self referencing mapping interferes with foreign key mapping

I have the following classes:
public class Track
{
public virtual int Id { get; set; }
public virtual Track MainMix { get; set; }
public virtual IEnumerable<Track> SubMixes { get; set; }
public virtual IList<FileVersion> Files { get; set; }
}
public class FileVersion
{
public virtual int Id { get; set; }
public virtual Track Track { get; set; }
}
And the following mappings:
public class TrackMap : ClassMap<Track>
{
public TrackMap()
{
Id(x=>x.Id);
References(x => x.MainMix);
HasMany(x => x.SubMixes)
.Inverse()
.Cascade.All()
.KeyColumn("MainMix_id");
HasMany(a => a.Files)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.All();
}
}
public class FileVersionMap : ClassMap<FileVersion>
{
public FileVersionMap()
{
Id(x => x.Id);
References(x => x.Track);
}
}
There is omitted code for the sake of simplicity. The Track table has a "MainMix_id" column that is a self referencing column for a parent/child relationship among Track records.
When I try to fetch a track from the database the NHProfiler tells me that Nhibernate tries to fetch the fileversions of that track with the following query:
SELECT files0_.MainMix_id as MainMix9_1_,
files0_.Id as Id1_,
files0_.Id as Id9_0_,
files0_.Track_id as Track8_9_0_
FROM [FileVersion] files0_
WHERE files0_.MainMix_id = 3 /* #p0 */
It seems like it has confused the parent id column of the Track table with its primary key column. When I remove References(x => x.MainMix) from the Track mapping the query is correct, but I don't have the parent track record returned.
Let me know if I can clarify this any more and thanks in advance for your help!
Does this make a difference?
TrackMap :
References(x => x.MainMix).Column("MainMix_id");
FileVersionMap :
References(x => x.Track).Column("Track_id");

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.

HNibernate 1 to Many Relationship Fluent NHiberate using only the Foreign Key Id

I'm wanting to have a 1 to many relationship in NHibernate where the Child table only has access to it's parentsId. Or the foreign key in the DB.
I've tried the following setup:
public class ParentTable
{
public ParentTable()
{
_childRecords = new List<ChildTable>();
}
public virtual int ParentId { get; set; }
private IList<ChildTable> _childRecords;
public virtual IEnumerable<ChildTable> ChildRecords
{
get { return _childRecords; }
}
public void AddChildTable(string value)
{
_childRecords.Add(new ChildTable{ StringField = value });
}
}
public class ChildTable
{
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int ParentId { get; set; }
}
Mappings:
public class ParentTableMap : ClassMap<ParentTable>
{
public ParentTableMap()
{
Not.LazyLoad();
Id(x => x.ParentId);
HasMany(x => x.ChildRecords)
.Not.LazyLoad()
.KeyColumn("ParentId").Cascade.All()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
}
}
public class ChildTableMap : ClassMap<ChildTable>
{
public ChildTableMap()
{
Not.LazyLoad();
Id(x => x.ChildTableId);
Map(x => x.StringField);
Map(x => x.ParentId).Not.Nullable();
}
}
The following test fails as it's trying to insert 0 into the ParentId column?
[TestFixture]
public class Tests
{
[Test]
public void SaveOrUpdate_ParentWithChildren_WillCreateParentWithChildRecordsHavingMatchingParentId()
{
int id;
using (var sessionForInsert = SessionProvider.SessionFactory.OpenSession())
{
using (var trx = sessionForInsert.BeginTransaction())
{
//Assign
var parent = new ParentTable();
parent.AddChildTable("Testing");
parent.AddChildTable("Testing2");
sessionForInsert.SaveOrUpdate(parent); // Fails here with DB constraint error
id = parent.ParentId;
}
}
using (var sessionForSelect = SessionProvider.SessionFactory.OpenSession())
{
//Action
var result = sessionForSelect.Get<ParentTable>(id);
Assert.AreEqual(id, result.ParentId);
Assert.AreEqual(id, result.ChildRecords.First().ParentId);
Assert.AreEqual(id, result.ChildRecords.Last().ParentId);
}
}
}
This is what it's trying to do:
exec sp_executesql N'INSERT INTO ChildTable (StringField, ParentId) VALUES (#p0, #p1); select SCOPE_IDENTITY()',N'#p0 nvarchar,#p1 int',#p0='Testing;,#p1=0
I realise I could set-up a reference to the Parent Class in the Child Class. However I'd like to avoid this if at all possible, due to circular references and the problems that will cause when serializing and de-serializing these classes.
Has anyone successfully set-up and 1 to many relationship like the above?
Thanks
Dave
I think you either need to:
Make the ParentId on ChildTable nullable, or
Change your id generators to something NHibernate can generate.
The second option is nice. Switch to Guid.Comb for your id's. There's a restriction on what object relational mappers can do. Specifically, it is recommended to let NHibernate generate the id's instead of the database. I think this (long) blog post explains it in detail: http://fabiomaulo.blogspot.com/2009/02/nh210-generators-behavior-explained.html.
Good luck!
The problem is that you are attempting to insert a parent and its children in one operation. To do this, NHibernate wants to insert the child records with a null ParentId then update ParentId after the parent record is inserted. This foreign key constraint causes this to fail.
The best solution is to map the relationship from child to parent. You don't have to publicly expose the parent, you could just expose its ParentId as int? if desired.
If that's unacceptable, you should be able to accomplish this by changing the order of operations. First, I would require the ParentId in ChildTable's constructor. Then change the operation order in the test to get it to pass.
public class ChildTable
{
public ChildTable(int parentId) { ParentId = parentId; }
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int ParentId { get; private set; }
}
using (var trx = sessionForInsert.BeginTransaction())
{
//Assign
var parent = new ParentTable();
sessionForInsert.Save(parent);
sessionForInsert.Flush(); // may not be needed
parent.AddChildTable("Testing");
parent.AddChildTable("Testing2");
trx.Commit();
id = parent.ParentId;
}
EDIT:
public class ChildTable
{
private ParentTable _parent;
public ChildTable(Parent parent) { _parent = parent; }
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int? ParentId
{
get { return _parent == null : null ? _parent.ParentId; }
}
}
public class ChildTableMap : ClassMap<ChildTable>
{
public ChildTableMap()
{
Not.LazyLoad();
Id(x => x.ChildTableId);
Map(x => x.StringField);
// From memory, I probably have this syntax wrong...
References(Reveal.Property<ParentTable>("Parent"), "ParentTableId")
.Access.CamelCaseField(Prefix.Underscore);
}
}

NHibernate sets Foreign Key in secondary update rather than on initial insert violating Not-Null Constrain on key column

I am having a problem with what should be a fairly simple (I would think) NHibernate use case.
I have a classic Parent and a Child entity like so:
public class Parent
{
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Child> Children { get; set; }
}
public class Child
{
public virtual int ChildId { get; set; }
public virtual Parent Parent { get; set; }
public virtual string Name { get; set; }
}
And mappings as follows:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Id(x => x.ParentId).GeneratedBy.Native();
Map(x => x.Name);
HasMany(x => x.Children).KeyColumn("ParentId").Cascade.SaveUpdate();
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Id(x => x.ChildId).GeneratedBy.Native();
Map(x => x.Name);
References(x => x.Parent).Column("ParentId").ReadOnly().Not.Nullable();
}
}
Lastly, I have a simple tests:
[Test]
public void Test_save_family()
{
var parent = new Parent();
var child = new Child {Parent = parent};
parent.Children = new List<Child>{child};
SessionManager.WithSession(
session =>
{
session.Save(parent);
session.Flush();
});
}
The test fails with a System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'ParentId'. This is correct in that the column is non-nullable but why is it inserting null?
If I remove the null constraint, the save works because NHibernate first inserts the parent, then inserts the child, then updates the ParentId column on the child record as shown in this output:
NHibernate: INSERT INTO [Parent] (Name) VALUES (#p0); select SCOPE_IDENTITY();#p0 = NULL
NHibernate: INSERT INTO [Child] (Name) VALUES (#p0); select SCOPE_IDENTITY();#p0 = NULL
NHibernate: UPDATE [Child] SET ParentId = #p0 WHERE ChildId = #p1;#p0 = 2, #p1 = 1
This seems bizarre to me as in almost all cases foreign key columns of this sort are declared non-nullable and hence the foreign key must be provided at insert. So why is NHibernate not setting the foreign key on the initial insert of the child row and how to I fix this?
A few problems with your mapping... You have a bidirectional relationship and NHibernate needs to know which way to update it. In the OO world, references only go one way and there is no way for NHibernate to know that Parent->Children is the same FK as Child->Parent. Right now you have Child->Parent set to ReadOnly(). This is telling NHibernate not to update this property. So it tries to insert the Child (with a null parent) and then update the FK from the Parent side. This isn't working if you have a not null constraint on your FK. The usual way to map this is to use Inverse=true on the parent side and let the child worry about persistence. (It's your job in the OO model to ensure that the Parent->Children collection contains the same set of references as the set of Child->Parent relationships.)
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Id(x => x.ParentId).GeneratedBy.Native();
Map(x => x.Name);
HasMany(x => x.Children).KeyColumn("ParentId").Inverse().Cascade.SaveUpdate();
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Id(x => x.ChildId).GeneratedBy.Native();
Map(x => x.Name);
References(x => x.Parent).Column("ParentId").Not.Nullable(); // Removed ReadOnly()
}
}
The SQL statements sent to the database when saving are now:
INSERT INTO [Parent]
(Name)
VALUES ('P1' /* #p0 */)
select SCOPE_IDENTITY()
INSERT INTO [Child]
(Name,
ParentId)
VALUES ('C1' /* #p0 */,
1 /* #p1 */)
select SCOPE_IDENTITY()

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
}
}