NHibernate using wrong key in queries, ObjectNotFoundByUniqueKeyException thrown - nhibernate

Using NHibernate 5.2, I'm getting an ObjectNotFoundByUniqueKeyException due to erroneous queries being executed with an invalid key when I do the following:
var session = sessionFactory.OpenSession();
var employee = new Employee(){
UserId = "joe",
Certifications = new List<Certification>()
};
employee.Certifications.Add(new Certification() { Employee = employee});
var id = session.Save(employee);
session.Flush();
session.Clear();
var emp = session.Get<Employee>(id);
foreach(var e in emp.Certifications)
{
Console.WriteLine(e.Id);
}
Queries executed
NHibernate: INSERT INTO Employee (UserId) VALUES (#p0); select SCOPE_IDENTITY();#p0 = 'joe' [Type: String (4000:0:0)]
NHibernate: INSERT INTO Certification (UserId) VALUES (#p0); select SCOPE_IDENTITY();#p0 = 'joe' [Type: String (4000:0:0)]
NHibernate: SELECT userquery_0_.Id as id1_0_0_, userquery_0_.UserId as userid2_0_0_ FROM Employee userquery_0_ WHERE userquery_0_.Id=#p0;#p0 = 1 [Type: Int32 (0:0:0)]
NHibernate: SELECT certificat0_.UserId as userid2_1_1_, certificat0_.Id as id1_1_1_, certificat0_.Id as id1_1_0_, certificat0_.UserId as userid2_1_0_ FROM Certification certificat0_ WHERE certificat0_.UserId=#p0;#p0 = 1 [Type: Int32 (0:0:0)]
NHibernate: SELECT userquery_0_.Id as id1_0_0_, userquery_0_.UserId as userid2_0_0_ FROM Employee userquery_0_ WHERE userquery_0_.UserId=#p0;#p0 = '1' [Type: String (4000:0:0)]
I'm expecting #p0 = 'joe' in the final two queries, not 1 and '1'. Can anyone see a problem with the mappings below that would explain this behavior?
Classes / Mappings
public class Employee
{
public virtual int Id { get; set; }
public virtual string UserId { get; set; }
public virtual ICollection<Certification> Certifications { get; set; }
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employee");
Id(x => x.Id);
Map(x => x.UserId);
HasMany(x => x.Certifications)
.KeyColumn("UserId")
.Cascade.All();
}
}
public class Certification
{
public virtual int Id { get; set; }
public virtual Employee Employee { get; set; }
}
public class CertificationMap : ClassMap<Certification>
{
public CertificationMap()
{
Table("Certification");
Id(x => x.Id);
References(x => x.Employee)
.Column("UserId")
.PropertyRef(x => x.UserId);
}
}

Collection is not using primary key of Employee, it is another property instead - PropertyRef. And we must inform one-to-many mapping as well as we do for many-to-one
HasMany(x => x.Certifications)
.KeyColumn("UserId")
.Cascade.All()
// use property, not the ID, when searching for my items
.PropertyRef("UserId")
;
A fact, that this mapping was missing, was causing the ID to be used (the magic 1) when NHibernate was loading collection

Related

fluent nhibernate hasmany relationship could not insert

I have the following tables:
LANDLORD = Id (Primary Key), FirstName, Surname, EmailAddress, Title;
PROPERTY = Id (Primary Key), Type, NumberOfBedrooms, Address1, Address2, City, County, PostCode, LandlordId (Foreign Key to Landlord entity);
My Domain classes are:
public class Landlord:BaseEntity
{
public virtual string Title { get; set; }
public virtual string Surname { get; set; }
public virtual string FirstName { get; set; }
public virtual string EmailAddress { get; set; }
public virtual IList<Property> Properties { get; set; }
public Landlord()
{
Properties = new List<Property>();
}
}
public class Property:BaseEntity
{
public virtual string Type { get; set; }
public virtual int NumberOfBedrooms { get; set;}
public virtual string Address1 { get; set; }
public virtual string Address2 { get; set; }
public virtual string City { get; set; }
public virtual string County { get; set; }
public virtual string PostCode { get; set; }
public virtual Landlord Landlord { get; set; }
}
My Fluent NHibernate maps are:
public sealed class LandlordMap:ClassMap<Landlord>
{
public LandlordMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Title);
Map(x => x.Surname);
Map(x => x.FirstName);
Map(x => x.EmailAddress);
HasMany(x => x.Properties)
.KeyColumns.Add("LandlordId")
.Inverse()
.Cascade.All();
Table("LANDLORD");
}
}
public sealed class PropertyMap:ClassMap<Property>
{
public PropertyMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Type);
Map(x => x.NumberOfBedrooms);
Map(x => x.Address1);
Map(x => x.Address2);
Map(x => x.City);
Map(x => x.County);
Map(x => x.PostCode);
References(x => x.Landlord, "LandlordId");
Table("PROPERTY");
}
}
To test that a Landlord is saved to the database, I have the following code:
public class Program
{
static void Main(string[] args)
{
ILandlordRepository rep = new LandlordRepository();
//Create property object
Property p1 = new Property
{
Address1 = "123 LA Road",
Address2 = "Bilston",
City = "Harlem",
County = "West Mids",
NumberOfBedrooms = 2,
PostCode = "wv134wd",
Type = "Bungalow"
};
//Create landlord and assign property
var landlord = new Landlord();
landlord.Title = "Dr";
landlord.FirstName = "Rohit";
landlord.Surname = "Kumar";
landlord.EmailAddress = "rkhkp#p.com";
landlord.Properties.Add(p1);
//Add to the database
rep.SaveOrUpdate(landlord);
Console.ReadKey();
}
}
When I call SaveOrUpdate I get this error:
could not insert: [Homes4U.Service.DomainClasses.Property][SQL: INSERT INTO PROPERTY (Type, NumberOfBedrooms, Address1, Address2, City, County, PostCode, LandlordId) VALUES (?, ?, ?, ?, ?, ?, ?, ?); select SCOPE_IDENTITY()]
Does anybody now why this is happening?
In your mappings, you've specified that the Property object is responsible for saving its reference to Landlord.
HasMany(x => x.Properties)
.KeyColumns.Add("LandlordId")
// Inverse means that the other side of the
// relationship is responsible for creating the reference
.Inverse()
.Cascade.All();
When you attempt to save the object, the Property object in the Properties collection has no reference to the landlord it belongs to. Under the hood, it'll attempt to insert a NULL into the LandlordId column.
This is why you are getting your error.
EDIT
To resolve, save the landlord first, without a reference to any property.
ILandlordRepository rep = new LandlordRepository();
IPropertyRepository pro = new PropertyRepository();
//Create landlord
var landlord = new Landlord();
landlord.Title = "Dr";
landlord.FirstName = "Rohit";
landlord.Surname = "Kumar";
landlord.EmailAddress = "rkhkp#p.com";
rep.SaveOrUpdate(landlord);
// Now add the landlord reference to the property and
// save
/Create property object
Property p1 = new Property
{
Address1 = "123 LA Road",
Address2 = "Bilston",
City = "Harlem",
County = "West Mids",
NumberOfBedrooms = 2,
PostCode = "wv134wd",
Type = "Bungalow",
Landlord = landlord
};
pro.SaveOrUpdate(p1);
You may need to reload the Landlord after saving it.
EDIT 2
See this question on Inverse.
Inverse Attribute in NHibernate

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

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

How to insert child element referencing foreign key in NHibernate?

I am still new to Fluent NHibernate. Not sure how I should approach this.
I have two entities:
public class Student
{
public virtual Guid StudentId { get; set; }
public virtual String Dept_id { get; set; }
public virtual String Name { get; set; }
public virtual int Age { get; set; }
public virtual String Address { get; set; }
public virtual Department Department { get; set; }
}
public class Department
{
public virtual int Dept_id { get; set; }
public virtual String Dept_name { get; set; }
public virtual IList<Student> Students { get; set; }
public Department()
{
Students = new List<Student>();
}
}
and the mappings are :
public class DepartmentMap : ClassMap<Department>
{
public DepartmentMap()
{
Table("Department");
Id(x => x.Dept_id).Column("Dept_id");
Map(x => x.Dept_name).Column("Dept_name");
HasMany(x => x.Students).KeyColumn("Student_id").Inverse()
.Cascade.All();
}
}
public class StudentMap :ClassMap<Student>
{
public StudentMap()
{
Table("Student");
Id(x => x.StudentId).Column("Student_id").GeneratedBy.GuidComb();
Map(x => x.Name);
Map(x => x.Age);
Map(x => x.Address);
Map(x => x.Dept_id).Column("Dept_id");
References(x => x.Department).Column("Dept_id").Not.Nullable();
}
}
when i am trying to insert as :
StudentRepository rep = new StudentRepository();
Student s = new Student();
s.Name = txtname.Text;
s.Age = int.Parse(txtage.Text);
s.Address = txtaddress.Text;
s.Dept_id = dddept.SelectedItem.Value;
rep.Add(s);
It's giving me error as:
{"not-null property references a null or transient value nHibernateTest.Domain.Student.Department"}
If you will be doing this like is in your post you will get an error because you want to send empty data of Department object. As Cole W says. You need to compare id of Department which you want to add with id of existing Department from database.
I will define SelectedDepartment from Cole W answer. You can add it to repository.
//repository
public Department SelectedDepartment(int id)
{
Department getDepartment = session.Get<Department>(id);
return getDepartment;
}
//controller
s.Department = rep.SelectedDepartment(1) //for example department with id = 1
When you are creating your new Student you need to set the Department not just the Id. Is there a reason you need a separate property for this? I would remove this property from your entity and from your mapping class and just leave in the Department reference. If you need the Department Id you can just reference it via Student.Department.Dept_id.
Basically you are trying to insert an entity with a null value in a foreign key column. You would need to change your creation to something like this:
StudentRepository rep = new StudentRepository();
Student s = new Student();
s.Name = txtname.Text;
s.Age = int.Parse(txtage.Text);
s.Address = txtaddress.Text;
s.Department = SelectedDepartment; //SelectedDepartment is the actual department entity from the database
rep.Add(s);

Fluent NHibernate Mapping is set to AllDeleteOrphan but is still trying to null the foreign key in the DB

I'm getting an error with NHibernate when I try to perfrom a ISession.Delete on any table with a One to Many relationship.
NHibernate is trying to set the foreign key to the parent table in the child table to null, rather than just deleting the child table row.
Here is my domain:
public class Parent
{
public Parent()
{
_children = new List<Child>();
}
public int Id { get; set; }
public string SimpleString { get; set; }
public DateTime? SimpleDateTime { get; set; }
private IList<Child> _children;
public IEnumerable<Child> Children
{
get { return _children; }
}
public void AddChild(Child child)
{
child.Parent = this;
_children.Add(child);
}
}
public class Child
{
public int Id { get; set; }
public string SimpleString { get; set; }
public DateTime? SimpleDateTime { get; set; }
[JsonIgnore]
public Parent Parent { get; set; }
}
I have set-up the Fluent NHibernate mappings as follows:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Not.LazyLoad();
Id(x => x.Id);
Map(x => x.SimpleString);
Map(x => x.SimpleDateTime);
HasMany(x => x.Children)
.Not.LazyLoad()
.KeyColumn("ParentId").Cascade.AllDeleteOrphan()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Not.LazyLoad();
Id(x => x.Id);
Map(x => x.SimpleString);
Map(x => x.SimpleDateTime);
References(x => x.Parent).Not.Nullable().Column("ParentId").Cascade.All().Fetch.Join();
}
}
I've told NHibernate to Cascade.AllDeleteOrphan() but it's still trying to set the ParentId foriegn key to null here is the test I setup:
public void Delete_GivenTableWithChildren_WillBeDeletedFromDB()
{
int id;
using (var createSession = MsSqlSessionProvider.SessionFactory.OpenSession())
{
var parent = new Parent();
parent.AddChild(new Child { SimpleString = "new child from UI" });
using (var trx = createSession.BeginTransaction())
{
createSession.Save(parent);
trx.Commit();
id = parent.Id;
}
}
using (var firstGetSession = MsSqlSessionProvider.SessionFactory.OpenSession())
{
var result = firstGetSession.Get<Parent>(id);
Assert.IsNotNull(result);
}
using (var deleteSession = MsSqlSessionProvider.SessionFactory.OpenSession())
{
using (var trx = deleteSession.BeginTransaction())
{
deleteSession.Delete("from " + typeof(Parent).Name + " o where o.Id = :Id", id, NHibernateUtil.Int32);
trx.Commit();
}
}
using (var session = MsSqlSessionProvider.SessionFactory.OpenSession())
{
var result = session.Get<Parent>(id);
Assert.IsNull(result);
}
}
Which is failing on the deleteSession.Delete line after attempting the following SQL:
exec sp_executesql N'UPDATE [Child] SET ParentId = null WHERE ParentId = #p0',N'#p0 int',#p0=5
with:
NHibernate.Exceptions.GenericADOException : could not delete collection: [SaveUpdateOrCopyTesting.Parent.Children#5][SQL: UPDATE [Child] SET ParentId = null WHERE ParentId = #p0]
----> System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'ParentId', table 'SaveUpdateCopyTestingDB.dbo.Child'; column does not allow nulls. UPDATE fails.
The statement has been terminated.
Does anyone know what I've done wrong in my mappings, or know of a way to stop NHibernate from attempting to null the foreign key id?
Thanks
Dave
Try setting .Inverse() on the ParentMap's HasMany, so it looks like:
HasMany(x => x.Children)
.Not.LazyLoad()
.KeyColumn("ParentId").Cascade.AllDeleteOrphan().Inverse()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
I'm not sure if cascade works if you delete with HQL.
Try this:
var parent = deleteSession.Load<Parent>(id)
deleteSession.Delete(parent);
It's a pity that you don't have lazy loading. Load would not need the entity to be read from the database, it would just create a proxy in memory.

Update/Insert without select

I have this very simple class
public class ProductAttributeValuePortal
{
public virtual int ID { get; set; }
public virtual Domain.Entity.Portals.ProductPortal Product { get; set; }
public virtual Attribute Attribute { get; set; }
public virtual string Value { get; set; }
}
with this very simple map
public ProductAttributeValueMap ()
{
Table("DM.dbo.ProductAttributeValues");
Id(x => x.ID, "ProductAttributeValue_id");
References(x => x.Product);
References(x => x.Attribute);
Map(x => x.Value);
}
Each time i make a insert NHibernate makes a Select of the attribute like :
NHibernate: INSERT INTO MachineData.dbo.ProductAttributeValues (Value, Product_id,
Attribute_id) VALUES (#p0, #p1, #p2); select SCOPE_IDENTITY();#p0 = '6745', #p1 = 39, #p2 = 'BSTD'
NHibernate: SELECT attribute_.Attribute_id, attribute_.Name as Name21_, attribute_.AttributeType as Attribut3_21_, attribute_.TagName as TagName21_, attribute_.MapTo as MapTo21_ FROM MachineShared.dbo.Attributes attribute_ WHERE attribute_.Attribute_id=#p0;#p0 = 'DLB'
What am i doing wrong. And where do i find some really uptodate books about nhibernate/Fluent nhibernate
How is ID assigned? If it is identity, then NHibernate has to go back to the DB to get the ID field.