Fluent Nhibernate Many-to-Many mapping with extra column - fluent-nhibernate

I want to map sth like this using fluent Nhibernate but I am not sure how to map the inventory table
This is the tables I have :
Product (Id,Name, ...)
Warehouse(Id, Name, ...)
Inventory(Product_id, Warehouse_id, StockInHand)
and Mappings like below
Public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.StoresStockedIn)
.Cascade.All()
.Inverse()
.Table("Inventory");
}
public WarehouseMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Products)
.Cascade.All()
.Table("Inventory");
}
The problem I face is that how can I map the StockInHand (how should the inventory model mapping?).
or are there other way to model this scenario ?
I have read some existing questions but not yet get clear understand what to do.
Thanks

Your relationship is not a many-to-many as far as NHibernate is concerned. A true many-to-many has no additional columns, such as StockInHand in your example.
You have to map this as two one-to-many relationships, and map Inventory as an entity.
Something like (i've skipped the other properties):
public class Product
{
public List<Inventory> Inventory { get; set; }
}
public class Warehouse
{
public List<Inventory> Inventory { get; set; }
}
public class Inventory
{
public Product Product { get; set; }
public Warehouse Warehouse { get; set; }
public bool StockInHand { get; set; }
}
public ProductMap() {
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Inventory)
.Cascade.All()
.Inverse()
.Table("Inventory");
}
public WarehouseMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Inventory)
.Cascade.All()
.Inverse()
.Table("Inventory");
}
public InventoryMap()
{
CompositeId()
.KeyReference(x => x.Product, "Product_id")
.KeyReference(x => x.Warehouse, "Warehouse_id")
Map(x => x.StockInHand);
}

Related

NHibernate HasMany List remains empty when querying

I've got a strange problem: Whenever I query an entity which has an IList, it remains empty. Querying the Language entity itself is possible... has this something to do with my composite key?
NHProf shows that BOTH entities are queried by sql, but for some reason, the results are not linked :-/
Here's some code:
public class Employee : User
{
public virtual string firstname { get; set; }
public virtual string lastname { get; set; }
public virtual string uid { get; set; }
public virtual string identity_provider
public virtual IList<Language> languages { get; set; }
}
The corresponding mapping:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("employee");
Not.LazyLoad();
CompositeId()
.KeyProperty(x => x.uid)
.KeyProperty(x => x.identity_provider);
Map(x => x.firstname);
Map(x => x.lastname);
HasMany<Language>(x => x.languages)
.Table("employee_spoken_language")
.KeyColumns.Add("employee_uid","employee_identity_provider")
.Inverse().Cascade.All();
}
}
And for completeness, this is how "Language" is mapped:
public class LanguageMap : ClassMap<SpokenLanguage>
{
public LanguageMap()
{
Table("employee_spoken_language");
Not.LazyLoad();
CompositeId()
.KeyProperty(x => x.employee_uid)
.KeyProperty(x => x.employee_identity_provider)
.KeyProperty(x => x.name);
References(x => x.knowledge_level).Column("knowledge_level");
}
}
No matter what I change, my Employee Entity always shows language = {}!? This is really driving me crazy and I cannot find the error. I made sure that the sql for querying languages is executed! I made sure that data is in the database. And because I am using fluent, I also checked the hmb created:
<bag cascade="all" inverse="true" name="languages" table="employee_spoken_language">
<key>
<column name="employee_uid" />
<column name="employee_identity_provider" />
</key>
<one-to-many class="Jobportal.Language, wcf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>
Hopefully someone can bring some light into this... Thanks in advance!
Regards,
Martin
This is an interesting one, I use nHibernate on all of my projects but rarely use composite keys. I used to do all of my mappings by hand (to learn) but now I use an Entity Developer tool to generate them. I have created comparable Employee and Language tables in my database with the same structure as yours and ran Entity Developer over them to generate the fluent mappings for you. I have listed these below. I don't see any vast difference but knowing how fussy nHibernate can be it might be worth a try. I may have some property lengths wrong etc but adjust as necessary to fit your specific needs.
For Employee:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Schema(#"dbo");
Table(#"Employees");
LazyLoad();
CompositeId()
.KeyProperty(x => x.Uid, set => {
set.Type("String");
set.ColumnName("uid");
set.Length(50);
set.Access.Property(); } )
.KeyProperty(x => x.IdentityProvider, set => {
set.Type("String");
set.ColumnName("identity_provider");
set.Length(50);
set.Access.Property(); } );
Map(x => x.Firstname)
.Column("firstname")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("varchar")
.Not.Nullable();
Map(x => x.Lastname)
.Column("lastname")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("varchar")
.Not.Nullable();
HasMany<Language>(x => x.Languages)
.Access.Property()
.AsSet()
.Cascade.None()
.LazyLoad()
.Inverse()
.Generic()
.KeyColumns.Add("uid", mapping => mapping.Name("uid")
.SqlType("varchar")
.Not.Nullable()
.Length(50))
.KeyColumns.Add("identity_provider", mapping => mapping.Name("identity_provider")
.SqlType("varchar")
.Not.Nullable()
.Length(50));
}
}
For Language:
public class LanguageMap : ClassMap<Language>
{
public LanguageMap()
{
Schema(#"dbo");
Table(#"Languages");
LazyLoad();
CompositeId()
.KeyProperty(x => x.Uid, set => {
set.Type("String");
set.ColumnName("uid");
set.Length(50);
set.Access.Property(); } )
.KeyProperty(x => x.IdentityProvider, set => {
set.Type("String");
set.ColumnName("identity_provider");
set.Length(50);
set.Access.Property(); } )
.KeyProperty(x => x.Name, set => {
set.Type("String");
set.ColumnName("name");
set.Length(50);
set.Access.Property(); } );
References(x => x.Employee)
.Class<Employee>()
.Access.Property()
.Cascade.None()
.LazyLoad()
.Columns("uid", "identity_provider");
}
}
Let me know if these work any better, if not I will generate some test data and try and run some queries against these tables using the mappings for you.
One other thing to consider is to make sure you are working with the latest version of nHibernate.

fluent nhibernate, unable to resolve property: <property>

I have a one-to-one relationship in my db and I seem to be having issues with my fluent nhibernate mapping of this relationship.
When I attempt to insert a new TableA entity I receive the following error: "unable to resolve property: TableA". The error is thrown on this line in the repository: int id = (int)_session.Save(item);
Repository code:
public T Save(T item)
{
try
{
_session.BeginTransaction();
int id = (int)_session.Save(item);
_session.Transaction.Commit();
return _session.Get<T>(id);
}
catch (Exception)
{
_session.Transaction.Rollback();
throw;
}
}
Table definitions:
Table A
Id (int, pk, identity)
Field1 (nvarchar)
Field2 (date)
...
Table B
TableAId (int, pk) <-- no fk constraint on these tables
Field1 (nvarchar)
Field2 (nvarchar)
Field3 (bit)
Classes:
public class TableA
{
public virtual int Id {get;set;}
public virtual string Field1 {get;set;}
public virtual DateTime Field2 {get;set;}
public virtual TableB TableB {get;set;}
}
public class TableB
{
public virtual int TableAId {get;set;}
public virtual string Field1 {get;set;}
public virtual string Field2 {get;set;}
}
Mapping:
public class TableAMap : ClassMap<TableA>
{
public TableAMap(){
Id(x => x.Id);
Map(x => x.Field1);
Map(x => x.Field2);
HasOne(x => x.TableB)
.Cascade.SaveUpdate()
.Fetch.Join();
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap()
{
Id(x => x.Id, "TableAId").GeneratedBy.Foreign("TableA");
Map(x => x.Field1);
Map(x => x.Field2);
Map(x => x.Field3);
}
}
I used these as a reference:
http://avinashsing.sunkur.com/2011/09/29/how-to-do-a-one-to-one-mapping-in-fluent-nhibernate/
One-to-one Mapping issue with NHibernate/Fluent: Foreign Key not updateing
IndexOutOfRangeException Deep in the bowels of NHibernate
I've been looking at this for so long now I fear I'm missing something simple (and stupid).
Update:
Tried this:
public class TableA
{
public virtual int Id {get;set;}
public virtual string Field1 {get;set;}
public virtual DateTime Field2 {get;set;}
public virtual TableB TableB {get;set;}
public virtual int TableBId
{
get{return TableB.Id;}
set{}
}
}
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
Id(x => x.Id);
Map(x => x.Field1);
Map(x => x.Field2);
HasOne<TableB>(x => x.TableB)
.Cascade.SaveUpdate()
.Fetch.Join()
.ForeignKey("TableBId");
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap()
{
Id(x => x.Id, "TableAId");
Map(x => x.Field1);
Map(x => x.Field2);
Map(x => x.Field3);
}
}
I didn't get the "unable to resolve property: TableA" error this time, but I received a different error. It seems like the Id of the TableA record did not cascade to the TableB record.
Did some more reading.
Found this post: fluent NHibernate one-to-one relationship?
Made some more changes:
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
Id(x => x.Id);
Map(x => x.Field1);
Map(x => x.Field2);
HasOne<TableB>(x => x.TableB)
.Cascade.All()
.Constrained()
.Fetch.Join()
.ForeignKey("TableBId");
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap()
{
Id(x => x.Id, "TableAId").UnsavedValue(0).GeneratedBy.Foreign("TableA");
Map(x => x.Field1);
Map(x => x.Field2);
Map(x => x.Field3);
HasOne<TableA>(x => x.TableA);
}
}
New error:
"attempted to assign id from null one-to-one property: TableA"
Even though this relationship is a one-to-one, and should be a one-to-one, I ended up modifying my database defintion to make this relationship a many-to-one and I mapped it as a many-to-one. Now it works. I'm not happy about it though, I think there should be a way to successfully map a one-to-one.
I was wasting too much time trying to get the one-to-one mapping to work and for the sake of my timeline I just needed to move on.

How is a Fluent Nhibernate Reference / HasMany relationship supposed to work?

I have two tables setup with a reference and hasmany relationship. When the Xref.SaveOrUpdate() is called an "Invalid index 7 for this SqlParameterCollection with Count=7." exception is thrown. I get the feeling I'm not setting up the HasMany and References correctly. What should I be doing differently?
public class PortalPhysicianMap : ClassMap<PortalPhysician>
{
public PortalPhysicianMap()
{
Table("PortalPhysicians");
Id(x => x.PhysicianRno).GeneratedBy.Identity();
Map(x => x.A1PhysicianRno);
Map(x => x.LastName);
Map(x => x.FirstName);
Map(x => x.TitleName);
Map(x => x.SuffixName);
Map(x => x.CorrName);
Map(x => x.Specialty);
Map(x => x.Institution);
Map(x => x.Addr1);
Map(x => x.Addr2);
Map(x => x.City);
Map(x => x.State);
Map(x => x.PostalCode);
Map(x => x.Country);
Map(x => x.Phone);
Map(x => x.Fax);
Map(x => x.InactiveDt);
Map(x => x.CreatedDate, "CreatedDt");
Map(x => x.UpdatedDate, "UpdatedDt");
HasMany(x => x.Xrefs)
.KeyColumn("A1PhysicianRno");
//.Cascade.All();
}
}
public class PortalLoginPhyXrefMap : ClassMap<PortalLoginPhyXref>
{
public PortalLoginPhyXrefMap()
{
Table("PortalLoginPhyXref");
Id(x => x.XrefRno).GeneratedBy.Identity();
Map(x => x.LoginRno);
Map(x => x.A1PhysicianRno);
Map(x => x.UserName);
Map(x => x.UserRole);
Map(x => x.InactiveDt);
Map(x => x.CreatedDate, "CreatedDt");
Map(x => x.UpdatedDate, "UpdatedDt");
References<PortalPhysician>(x => x.Login)
.Column("LoginRno");
}
}
using (ISession s = Env.dbPortal.OpenSession())
{
try
{
using (ITransaction Trans = s.BeginTransaction())
{
Trans.Begin();
foreach (PortalLogin Login in lstLogins)
{
if (Login.UserName != null)
{
Login.SaveOrUpdate(s);
foreach (PortalLoginPhyXref Xref in Login.Xrefs)
{
Xref.LoginRno = Login.LoginRno;
Xref.SaveOrUpdate(s);
}
}
}
Trans.Commit();
}
}
catch (Exception Ex)
{
frmError.Show(Ex);
}
}
Additional code showing what happens in the SaveOrUpdate()
public class PortalLoginPhyXref : BaseRec
{
public virtual void SaveOrUpdate(ISession Sess)
{
base.SaveOrUpdate(Sess, XrefRno);
}
}
public abstract class BaseRec
{
public virtual DateTime CreatedDate { get; set; }
public virtual string CreatedUserID { get; set; }
public virtual DateTime? UpdatedDate { get; set; }
public virtual string UpdatedUserID { get; set; }
public virtual void SaveOrUpdate(ISession Sess, int Rno)
{
SaveOrUpdate(Sess, (Rno == 0));
}
public virtual void SaveOrUpdate(ISession Sess, string ID)
{
SaveOrUpdate(Sess, (ID == null));
}
private void SaveOrUpdate(ISession Sess, bool Save)
{
if (Save)
{
CreatedDate = DateTime.Now;
CreatedUserID = Env.UserID;
}
UpdatedDate = DateTime.Now;
UpdatedUserID = Env.UserID;
Sess.SaveOrUpdate(this);
}
}
You are mapping the same column twice in your mapping above. That is what generally causes the Invalid index 7 for this SqlParameterCollection with Count=7 error.
Here is an example of where you are doing this:
Map(x => x.LoginRno);
References<PortalPhysician>(x => x.Login)
.Column("LoginRno");
You should not be doing Map(x => x.LoginRno)
Login.SaveOrUpdate(s); // save or update transient login object with all referenced objects
foreach (PortalLoginPhyXref Xref in Login.Xrefs)
{
Xref.LoginRno = Login.LoginRno;
// Xref.SaveOrUpdate(s); // They'll update when you close transaction.
}

Fluent Nhibernate: Mapping Issue

I am very much new to Fluent Nhibernate. I am stuck with the one situation.
Please find bellow details about it.
Our table structure is like as
Table Student { Student_Id, Name}
Table School { School_Id, Name}
Table LinkTable { School_Id, Student_Id}
LinkTable contains only id of the Student and School. [Composite Key]
Relation is like
1) One student can be part of 0 or 1 School.
2) One School can contains many students.
Can anyone please let me know how the mapping will be done for each file?
or let mw know what is wrong in following mapping files
Right now, it is giving me error that Student Property is not found on SchoolStudent.
public Student()
{
Id(x => x.Id);
Map(x => x.Name);
HasOne(x => x.SchoolStudent).PropertyRef(r => r.Student);
}
public School()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.SchoolStudent).KeyColumn("School_Id").Inverse().Cascade.AllDeleteOrphan();
}
public SchoolStudent()
{
CompositeId().KeyReference(x => x.School, "School_Id")
.KeyReference(x => x.Student, "Student_Id");
}
Thanks,
Mahesh
I would re-write it to something like this:
Student.cs
public class Student
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<School> Schools { get; set; }
public Student()
{
Schools = new List<School>();
}
}
School.cs
public class School
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Student> Students { get; set; }
public School()
{
Students = new List<Student>();
}
}
StudentMap.cs
public class StudentMap : ClassMap<Student>
{
public Student()
{
Id(x => x.Id , "Student_Id");
Map(x => x.Name , "Name");
HasManyToMany(x => x.Schools).Table("LinkTable")
.ParentKeyColumn("Student_Id")
.ChildKeyColumn("School_Id");
}
}
SchoolMap.cs
public class SchoolMap: ClassMap<School>
{
public School()
{
Id(x => x.Id , "School_Id");
Map(x => x.Name , "Name");
HasManyToMany(x => x.Students).Table("LinkTable")
.ParentKeyColumn("School_Id")
.ChildKeyColumn("Student_Id");
}
}
If a student can only be associated with 0 or 1 schools, then you might consider dropping the LinkTable and the SchoolStudent class and just add School_Id to the Student table and a reference to School in the Student class.
Then your mappings are like:
public Student()
{
Id(x => x.Id);
Map(x => x.Name);
References(x => x.School);
}
public School()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Student).Inverse().Cascade.AllDeleteOrphan();
}

Nhibernate Component Mapping : Parent Object null in Value Object while querying from database

I am mapping my value Object Item as component withthe folowing mapping configuration
{
Table("Product");
Not.LazyLoad();
Id(x => x.Id, "id");
Map(x => x.Number, "number");
Map(x => x.Name, "name");
Map(x => x.Description, "description");
Map(x => x.Status, "status");
HasMany(x => x.ItemLines).Component(
m =>
{
m.Map(x => x.ItemId, "itemid");
m.Map(x => x.Qty, "quantity");
}).Table("productitems").KeyColumn("itemid");
}
Class structure
public class ItemLine
{
public Product Product { get; set; }
public Guid ItemId { get; set; }
public int Qty { get; set; }
public ItemLine()
{
}
public ItemLine(Product product, Guid itemId, int qty)
{
Product = product;
ItemId = itemId;
Qty = qty;
}
//Equality and GetHashCode implemented.....
}
I am able to insert data to Database but while retrieving back by Product Id, the Product property in Item Line is null.
Do I need to pass any References in Mapping>
Please help
Thank you,
Mar
Ok. Solved by trial and error.
Add m.ParentReference(x => x.Product);
{
Table("Product");
Not.LazyLoad();
Id(x => x.Id, "id");
Map(x => x.Number, "number");
Map(x => x.Name, "name");
Map(x => x.Description, "description");
Map(x => x.Status, "status");
HasMany(x => x.ItemLines).Component(
m =>
{
m.Map(x => x.ItemId, "itemid");
m.Map(x => x.Qty, "quantity");
m.ParentReference(x => x.Product);
}).Table("productitems").KeyColumn("itemid");
}
hope this helps someone.