NHibernate HasMany List remains empty when querying - nhibernate

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.

Related

Sharing field as composite key and foreign key in Fluent NHibernate

Consider following mapping code please :
public sealed class BankMapping : ClassMap<Bank>
{
public BankMapping()
{
CompositeId()
.KeyProperty(x => x.SerialNumber, "SerialBankRef")
.KeyProperty(x => x.FinancialPeriodId, "FinancialPeriodRef");
Map(x => x.Code);
Map(x => x.Title);
Map(x => x.Comment);
Map(x => x.IsActive);
HasMany(x => x.BankBranchs).KeyColumns.Add(new[] { "SerialBankRef", "FinancialPeriodRef" });
}
}
And the BankBranch mapping code is :
public sealed class BankBranchMapping : ClassMap<BankBranch>
{
public BankBranchMapping()
{
CompositeId()
.KeyProperty(x => x.FinancialPeriodId, "FinancialPeriodRef")
.KeyProperty(x => x.SerialNumber, "SerialNumber");
Map(x => x.Code);
Map(x => x.Title);
Map(x => x.Comment);
Map(x => x.IsActive);
Map(x => x.Address);
Map(x => x.Fax);
Map(x => x.Telephone);
References(x => x.Bank).Columns(new[] { "SerialBankRef", "FinancialPeriodRef" });
}
}
As you can see the FinancialPeriodRef field acts as foreign key and part of composite key in the BankBranch mapping and NHibernate builds DB correctly and everything seems to be fine until I try to insert a record in the BankBranch table.
The error System.IndexOutOfRangeException : Invalid index 10 for this SqlParameterCollection with Count=10. which indicates I've mapped a field (FinancialPeriodRef) twice as FK and PK appears and I have no idea how to fix the problem.
I need FinancialPeriodRef in the BankBranch as part of primary key while it's absolutely equal to FinancialPeriodRef from Bank.
I need this field to establish unique constraint and also benefits of composite key is essential.
You need to use KeyReference rather than KeyProperty to describe composite foreign keys.
public BankBranchMapping()
{
CompositeId()
.KeyReference(x => x.FinancialPeriodId, "FinancialPeriodRef")
.KeyReference(x => x.SerialNumber, "SerialNumber");
...
}
I had the exact same problem as you and after an hour or so came across this post: https://stackoverflow.com/a/7997225/569662 which pointed me right.

Fluent Nhibernate Many-to-Many mapping with extra column

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

Fluent NHibernate HasManyToMany() Save/Update Problem

i have the following code, which is supposed to give specific functionality but it isn't :S
anyway, here's my problem:
http://img525.imageshack.us/img525/1315/diagramp.png
here's the mapping code:
public class UsersMap : ClassMap<User>
{
public UsersMap()
{
this.Table("Users");
Id(x => x.UserName).GeneratedBy.Assigned();
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.Password);
Map(x =>x.EMail);
Map(x => x.Title);
Map(x => x.Division);
Map(x => x.Status);
HasManyToMany(x => x.Roles)
.Table("UserInRoles").ParentKeyColumn("Username")
.ChildKeyColumn("RoleId").AsBag().Inverse();
}
}
public class RolesMap : ClassMap<Role>
{
public RolesMap()
{
this.Table("Roles");
Id(x => x.ID).GeneratedBy.Assigned();
Map(x => x.RoleName);
HasManyToMany(x => x.Users)
.Table("UserInRoles").ParentKeyColumn("RoleId")
.ChildKeyColumn("Username").AsBag().Cascade.All();
}
}
my problem is when trying to (assign a Role for specific user) the UserName is added to table UserInRoles but the Role ID if it is already existing it will be removed from its corrosponding row and assigned to the new row, any idea ?
Have you looked making your ManyToMany work by convention?
Linked example actually refers to Roles and pretty much what you are trying to do here.

Abort due to constraint violation columns GroupId, idx are not unique

I'm using FluentNHibernate and have done a many-to-many mapping but when I try to save my entity I get the following error:
NHibernate.Exceptions.GenericADOException: NHibernate.Exceptions.GenericADOException
: could not insert collection: [Test.Entities.Recipient.Groups#b6815d34-f436-4142-9b8e-1bfcbf25509e][SQL: SQL not available]
---- System.Data.SQLite.SQLiteException : Abort due to constraint violation
columns GroupId, idx are not unique
Here is my mapping:
public class GroupMap : ClassMap<Group>
{
public GroupMap()
{
Id(x => x.Id).GeneratedBy.Guid();
Map(x => x.Name);
Map(x => x.SenderName);
Map(x => x.Created);
HasManyToMany(x => x.Recipients)
.AsList()
.WithTableName("groups_recipients")
.WithParentKeyColumn("GroupId")
.WithChildKeyColumn("RecipientId")
.LazyLoad()
.Cascade.AllDeleteOrphan();
}
}
public class RecipientMap : ClassMap<Recipient>
{
public RecipientMap()
{
Id(x => x.Id).GeneratedBy.Guid();
Map(x => x.Firstname);
Map(x => x.Lastname);
Map(x => x.Phone);
Map(x => x.Email);
HasManyToMany(x => x.Groups)
.AsList()
.WithTableName("groups_recipients")
.WithParentKeyColumn("RecipientId")
.WithChildKeyColumn("GroupId")
.LazyLoad().Cascade.None();
}
}
The problem seems to have something to do with the relationship tables id but I can't figure out how to solve it.
Cheers,
nandarya
Using AsList() was not a good idea. Should be AsBag(). And everything seems to work.

Fluent NHibernate Mapping Issue

I am trying to map several tables using Fluent Nhibernate. My tests are giving me the following error:
NHibernate.Exceptions.GenericADOException: could not initialize a collection: [FluentWeb.Domain.Employees.Orders#1]
I am trying to map a one to many relationship between Employees and Orders. Orders then has a many to many relation ship with Products (NorthWind). Here are my mappings.. Can someone give me a hand. All was working with HBMs
public class EmployeeMap : ClassMap<Employees>
{
public EmployeeMap()
{
Id(x => x.EmployeeID);
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.City);
Map(x => x.HireDate);
Map(x => x.Title);
HasMany(x => x.Orders)
.AsBag().WithForeignKeyConstraintName("EmployeeID")
.Inverse()
.Cascade.All();
}
}
public class OrdersMap : ClassMap<Orders>
{
public OrdersMap()
{
Id(x => x.OrderID);
Map(x => x.OrderDate);
Map(x => x.RequiredDate);
Map(x => x.ShippedDate);
References(x => x.Employee);
HasManyToMany(x => x.Products)
.Cascade.All()
.WithTableName("Order Details");
}
}
public class ProductsMap : ClassMap<Products>
{
public ProductsMap()
{
Id(x => x.ProductID);
Map(x => x.ProductName);
Map(x => x.UnitPrice);
HasManyToMany(x => x.Orders)
.Cascade.All()
.Inverse()
.WithTableName("Order Details");
}
}
Please let me know if more information is needed. Thanks for the help!
-Nick
Spaces in table names is usually a pretty bad idea. If you can't remove them, try surrounding the table names in backticks, which is NHibernate's database agnostic escaping mechanism.
.WithTableName("`Order Details`');