fluent nhibernate hasmany relationship could not insert - nhibernate

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

Related

NHibernate using wrong key in queries, ObjectNotFoundByUniqueKeyException thrown

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

Nhibernate databinding foreign key to lookupedit

I would like to databind the foreign key property Product.CategoryId to a Devexpess Lookupedit in Windows Forms Application.
So
lookEditCategory.DataBindings
.Add(new Binding("EditValue", Product, "CategoryId ", true,
DataSourceUpdateMode.OnPropertyChanged));
lookEditCategory.Properties.Columns.Clear();
lookEditCategory.Properties.NullText = "";
lookEditCategory.Properties.DataSource = CatCol;
lookEditCategory.Properties.ValueMember = "CategoryId";
lookEditCategory.Properties.DisplayMember = "CategoryName";
var col = new LookUpColumnInfo("CategoryName") { Caption = "Type" };
lookEditCategory.Properties.Columns.Add(col);
The problem is that Nhibernate does not expose the foreign key Product.CategoryId. Instead my entity and mapping are like this
public partial class Product
{
public virtual int ProductId { get; set; }
[NotNull]
[Length(Max=40)]
public virtual string ProductName { get; set; }
public virtual bool Discontinued { get; set; }
public virtual System.Nullable<int> SupplierId { get; set; }
[Length(Max=20)]
public virtual string QuantityPerUnit { get; set; }
public virtual System.Nullable<decimal> UnitPrice { get; set; }
public virtual System.Nullable<short> UnitsInStock { get; set; }
public virtual System.Nullable<short> UnitsOnOrder { get; set; }
public virtual System.Nullable<short> ReorderLevel { get; set; }
private IList<OrderDetail> _orderDetails = new List<OrderDetail>();
public virtual IList<OrderDetail> OrderDetails
{
get { return _orderDetails; }
set { _orderDetails = value; }
}
public virtual Category Category { get; set; }
public class ProductMap : FluentNHibernate.Mapping.ClassMap<Product>
{
public ProductMap()
{
Table("`Products`");
Id(x => x.ProductId, "`ProductID`")
.GeneratedBy
.Identity();
Map(x => x.ProductName, "`ProductName`")
;
Map(x => x.Discontinued, "`Discontinued`")
;
Map(x => x.SupplierId, "`SupplierID`")
;
Map(x => x.QuantityPerUnit, "`QuantityPerUnit`")
;
Map(x => x.UnitPrice, "`UnitPrice`")
;
Map(x => x.UnitsInStock, "`UnitsInStock`")
;
Map(x => x.UnitsOnOrder, "`UnitsOnOrder`")
;
Map(x => x.ReorderLevel, "`ReorderLevel`")
;
HasMany(x => x.OrderDetails)
.KeyColumn("`ProductID`")
.AsBag()
.Inverse()
.Cascade.None()
;
References(x => x.Category)
.Column("`CategoryID`");
}
}
}
I cannot add the property CategoryID in my Product entity and mapping because then it will be mapped twice.
Is there any solution?
Yes. Do NOT use your domain entities in the UI.
Sometimes your UI doesn't need (and shouldn't be aware of) all the properties of your domain objects.
Other times, it needs DTOs that contain data from different domain sources (for example- a list of CourseNames for the Student screen), or, like in your case- it needs the data to be represented in a slightly different way.
So the best way would be to create your DTOs with all (and only) the properties needed by the UI.
See this SO question for further details.

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);

How to enable LazyLoad in Fluent NHibernate?

I'm testing Fluent NHibernate with NorthWind database. Now, I've created Employee and EmployeeMap class. Source code is like below.
class Employee
public class Employee
{
public virtual int EmployeeID { get; private set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual string Title { get; set; }
public virtual string TitleOfCourtesy { get; set; }
public virtual DateTime? BirthDate { get; set; }
public virtual DateTime? HireDate { get; set; }
public virtual string Address { get; set; }
public virtual string City { get; set; }
public virtual string Region { get; set; }
public virtual string PostalCode { get; set; }
public virtual string Country { get; set; }
public virtual string HomePhone { get; set; }
public virtual string Extension { get; set; }
public virtual string Notes { get; set; }
public virtual Employee ReportsTo { get; set; }
public virtual string PhotoPath { get; set; }
public virtual IList<Territory> Territories{ get; set; }
public Employee()
{
Territories = new List<Territory>();
}
public virtual void AddTerritory(Territory territory)
{
territory.Employees.Add(this);
this.Territories.Add(territory);
}
}
class EmployeeMap
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employees");
Id(x => x.EmployeeID);
Map(x => x.LastName);
Map(x => x.FirstName);
Map(x => x.Title);
Map(x => x.TitleOfCourtesy);
Map(x => x.BirthDate);
Map(x => x.HireDate);
Map(x => x.Address);
Map(x => x.City);
Map(x => x.Region);
Map(x => x.PostalCode);
Map(x => x.Country);
Map(x => x.HomePhone);
Map(x => x.Extension);
Map(x => x.Notes);
Map(x => x.PhotoPath);
References(x => x.ReportsTo).Column("ReportsTo").LazyLoad();
HasManyToMany(x => x.Territories)
.Cascade.All()
.Table("EmployeeTerritories")
.ParentKeyColumn("EmployeeID")
.ChildKeyColumn("TerritoryID");
}
}
Then I try to load all employees from database, but all employees have reference object on ReportsTo property.
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (session.BeginTransaction())
{
Console.WriteLine("All employees");
var emp_ = session.CreateCriteria(typeof(Employee));
var employees = emp_.List<Employee>();
foreach (var employee in employees)
{
Console.WriteLine(employee.FirstName); // every employee has reference object on ReportsTo property here.
}
Console.Write("--------");
}
}
I want to know, what wrong with my code and how to fixed it?
Lazy Load is enabled by default. The reference in ReportsTo is a proxy that will only be loaded from the DB if any property other than the ID is used.
Lazy loading is not enabled by default.
public EmployeeMap()
{
Table("Employees");
LazyLoad();
// etc.
}
That being said, testing with a foreach statement can be mischievous, because even if you have enabled lazy loading, the second you query for "employee.FirstName", NHibernate will hit the database and return the results. You're better off catching NHibernate's generated SQL or just using the NHibernate Profiler.

Fluent NHibernate Many to one mapping

I am creating a NHibenate application with one to many relationship. Like City and State data.
City table
CREATE TABLE [dbo].[State](
[StateId] [varchar](2) NOT NULL primary key,
[StateName] [varchar](20) NULL)
CREATE TABLE [dbo].[City](
[Id] [int] primary key IDENTITY(1,1) NOT NULL ,
[State_id] [varchar](2) NULL refrences State(StateId),
[CityName] [varchar](50) NULL)
My mapping is follows
public CityMapping()
{
Id(x => x.Id);
Map(x => x.State_id);
Map(x => x.CityName);
HasMany(x => x.EmployeePreferedLocations)
.Inverse()
.Cascade.SaveUpdate();
References(x => x.State)
//.Cascade.All();
//.Class(typeof(State))
//.Not.Nullable()
.Cascade.None()
.Column("State_id");
}
public StateMapping()
{
Id(x => x.StateId)
.GeneratedBy.Assigned();
Map(x => x.StateName);
HasMany(x => x.Jobs)
.Inverse();
//.Cascade.SaveUpdate();
HasMany(x => x.EmployeePreferedLocations)
.Inverse();
HasMany(x => x.Cities)
// .Inverse()
.Cascade.SaveUpdate();
//.Not.LazyLoad()
}
Models are as follows:
[Serializable]
public partial class City
{
public virtual System.String CityName { get; set; }
public virtual System.Int32 Id { get; set; }
public virtual System.String State_id { get; set; }
public virtual IList<EmployeePreferedLocation> EmployeePreferedLocations { get; set; }
public virtual JobPortal.Data.Domain.Model.State State { get; set; }
public City(){}
}
public partial class State
{
public virtual System.String StateId { get; set; }
public virtual System.String StateName { get; set; }
public virtual IList<City> Cities { get; set; }
public virtual IList<EmployeePreferedLocation> EmployeePreferedLocations { get; set; }
public virtual IList<Job> Jobs { get; set; }
public State()
{
Cities = new List<City>();
EmployeePreferedLocations = new List<EmployeePreferedLocation>();
Jobs = new List<Job>();
}
//public virtual void AddCity(City city)
//{
// city.State = this;
// Cities.Add(city);
//}
}
My Unit Testing code is below.
City city = new City();
IRepository<State> rState = new Repository<State>();
Dictionary<string, string> critetia = new Dictionary<string, string>();
critetia.Add("StateId", "TX");
State frState = rState.GetByCriteria(critetia);
city.CityName = "Waco";
city.State = frState;
IRepository<City> rCity = new Repository<City>();
rCity.SaveOrUpdate(city);
City frCity = rCity.GetById(city.Id);
The problem is , I am not able to insert record. The error is below.
"Invalid index 2 for this SqlParameterCollection with Count=2."
But the error will not come if I comment State_id mapping field in the CityMapping file. I donot know what mistake is I did. If do not give the mapping Map(x => x.State_id); the value of this field is null, which is desired. Please help me how to solve this issue.
Few remarks:
Remove this State_id property from the City class and the mapping. You already have a State property so it makes no sense in your object model.
Those Jobs and EmployeePreferedLocations properties in the State class don't have any related columns/tables in your database (at least the one you've shown here) while you have mappings for them.