I am new to NHibernate. I have created two tables in sql server like UserDetails and its corresponding Products table. The mapping are like!
My entity classes
public class UserDetails
{
[Required(ErrorMessage = "Please enter User Id")]
public virtual string UserId { get; set; }
[Required(ErrorMessage = "Please enter User Name")]
public virtual string UserName { get; set; }
[Required(ErrorMessage = "Please enter Password")]
public virtual string Password { get; set; }
[Required(ErrorMessage = "Please enter Email Id")]
[EmailAddress(ErrorMessage = "Please enter proper Email Id")]
public virtual string EmailId { get; set; }
[Required(ErrorMessage = "Please enter Address")]
[DataType(DataType.MultilineText)]
public virtual string Address { get; set; }
[Required(ErrorMessage = "Please enter Phone No")]
public virtual string PhoneNo { get; set; }
public virtual ISet<Product> Products { get; set; }
}
public class Product
{
[HiddenInput(DisplayValue=false)]
public virtual int ProductID { get; set; }
[Required(ErrorMessage="Please enter a product name")]
public virtual string Name { get; set; }
[DataType(DataType.MultilineText)]
[Required(ErrorMessage = "Please enter a description")]
public virtual string Description { get; set; }
[Required]
[Range(0.01,double.MaxValue,ErrorMessage="Please enter a positive price")]
public virtual decimal Price { get; set; }
[Required(ErrorMessage = "Please specify a category")]
public virtual string Category { get; set; }
public virtual byte[] ImageData { get; set; }
[HiddenInput(DisplayValue=false)]
public virtual string ImageMimeType { get; set; }
public virtual UserDetails UserDetail { get; set; }
}
Mapping
UserDetails.hbm.xml
<class name="SportsStore.Domain.Entities.UserDetails,SportsStore.Domain" table="UserDetails" lazy="true" >
<id name="UserId" column="UserId" access="property" type="String" >
<generator class="assigned"></generator>
</id>
<property name="UserName" column="UserName" access="property" type="String" ></property>
<property name="Password" column="Password" access="property" type="String"></property>
<property name="EmailId" column="EmailId" access="property" type="String" ></property>
<property name="Address" column="Address" access="property" type="String" ></property>
<property name="PhoneNo" column="PhoneNo" access="property" type="String" ></property>
<set name="Products" lazy="true" cascade="all-delete-orphan" inverse="true" >
<key column="UserId" ></key>
<one-to-many class="SportsStore.Domain.Entities.Product"></one-to-many>
</set>
Products.hbm.xml
<class name="SportsStore.Domain.Entities.Product,SportsStore.Domain" table="Products">
<id name="ProductID" column="ProductID" access="property" type="Int32" >
<generator class="native"></generator>
</id>
<property name="Name" column="Name" access="property" type="String">
</property>
<property name="Description" column="Description" access="property" type="String"></property>
<property name="Price" column="Price" access="property" type="decimal"></property>
<property name="Category" column="Category" access="property" type="String"></property>
<property name="ImageData" column="ImageData" access="property" type="BinaryBlob" length="2147483647"></property>
<property name ="ImageMimeType" column="ImageMimeType" access="property" type="String" ></property>
<many-to-one name="UserDetail" column="UserId" class="SportsStore.Domain.Entities.UserDetails" access="property" cascade="all"></many-to-one>
var user = (from userDetails in _session.Query<UserDetails>()
where userDetails.UserId == userId && userDetails.Password == password
select userDetails);
I thought I will get data from only userdetails as lazy loading is applied to it, but on debug mode I can see data from products table too. why? am I understood lazy loading in wrong way or is there any bug in my mapping? According to my knowledge lazy loading loads the data only on demand means if we iterate then the data gets loaded from products table.
Your assumption:
...According to my knowledge lazy loading loads the data only on demand means if we iterate then the data gets loaded from products table
Is absolutely correct. There is no but. Only on demand, for example if we want to iterate them. And exactly that is happening in the debug window.
Debug window is application as some other winform, exe, wpf... It simply prvides native/built in UI to observe our objcets. And once we start to observe/iterate them - they realize that there is demand - data from products table is loaded.
How to be sure?
Not so complicated. Just before starting to observe any object in the Debug window, call: session.Clear(). From that moment, only stuff already loaded will be available later.
So, in debug window, we should now see some exception about lazy loading failure...
Related
I've spent a couple of days researching this on Google, StackOverflow and reading various blogs on this but to no avail. My question is if a collection is updated within an entity then would this cause NHibernate to update all properties in the entity of the modified collections? In this case I've added a user to a role and once I call session.SaveOrUpdate then 2 updates occur (NHibernate updates user and role) then the INSERT occurs. Is this the default behavior? I've tried to do the following to see if I can get NHibernate to just issue the INSERT statement:
Ran a Ghostbuster test on these entities based on code by Jason Dentler and Fabio Maulo but everything comes back ok and there are no dirty properties.
I made properties nullable that are defined as null in the database.
Set Inverse true on one of the entites.
Any help or insight is much appreciated.
Here are the mapping and class files.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Custom.NHibernateLib.Model" assembly="Custom.NHibernateLib" xmlns="urn:nhibernate-mapping-2.2">
<class name="Users">
<id name="UserId" type="Int64">
<generator class="hilo" />
</id>
<property name="ApplicationName" type="String" length="50" />
<property name="Username" type="String" length="15" />
<property name="Email" type="String" length="15" />
<property name="Password" type="String" length="50" />
<property name="PasswordSalt" type="String" length="128" />
<property name="PasswordQuestion" type="String" length="15" />
<property name="PasswordAnswer" type="String" length="50" />
<property name="IsApproved" type="YesNo" />
<property name="LastActivityDate" type="DateTime" />
<property name="LastLoginDate" type="DateTime" />
<property name="LastPasswordChangedDate" type="DateTime" />
<property name="CreationDate" type="DateTime" />
<property name="IsOnline" type="YesNo" />
<property name="IsAnonymous" type="YesNo" />
<property name="IsLockedOut" type="YesNo" />
<property name="LastLockedOutDate" type="DateTime" />
<property name="FailedPasswordAttemptCount" type="Int32" />
<property name="FailedPasswordAttemptWindowStart" type="DateTime" />
<property name="FailedPasswordAnswerAttemptCount" type="Int32" />
<property name="FailedPasswordAnswerAttemptWindowStart" type="DateTime" />
<property name="Comment" type="String" length="4001" />
<bag name="RoleList" table="UserRoles" lazy="true" cascade="save-update, persist" batch-size="10">
<key column="UserId" not-null="true" />
<many-to-many class="Roles" foreign-key="FK_Roles_UserRoles_RoleId">
<column name="RoleId" not-null="true" />
</many-to-many>
</bag>
<one-to-one name="Profiles" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Custom.NHibernateLib.Model" assembly="Custom.NHibernateLib" xmlns="urn:nhibernate-mapping-2.2">
<class name="Roles">
<id name="RoleId" type="Int64">
<generator class="hilo" />
</id>
<property name="ApplicationName" type="String" length="50" />
<property name="RoleName" type="String" length="50" />
<bag name="UserList" table="UserRoles" lazy="true" inverse="true" cascade="save-update, persist" batch-size="10">
<key column="RoleId" not-null="true" />
<many-to-many class="Users" foreign-key="FK_User_UserRoles_UserId">
<column name="UserId" not-null="true" />
</many-to-many>
</bag>
</class>
</hibernate-mapping>
Here are the class files:
using System;
using System.Collections.Generic;
namespace Custom.NHibernateLib.Model
{
public class Users : Entity
{
public Users (){}
public virtual long UserId { get; set; }
public virtual string ApplicationName { get; set; }
public virtual string Username { get; set; }
public virtual string Email { get; set; }
public virtual string Password{ get; set; }
public virtual string PasswordSalt { get; set; }
public virtual string PasswordQuestion { get; set; }
public virtual string PasswordAnswer { get; set; }
public virtual bool? IsApproved { get; set; }
public virtual DateTime? LastActivityDate { get; set; }
public virtual DateTime? LastLoginDate { get; set; }
public virtual DateTime? LastPasswordChangedDate { get; set; }
public virtual DateTime? CreationDate { get; set; }
public virtual bool? IsOnline { get; set; }
public virtual bool? IsAnonymous { get; set; }
public virtual bool? IsLockedOut { get; set; }
public virtual DateTime? LastLockedOutDate { get; set; }
public virtual int? FailedPasswordAttemptCount { get; set; }
public virtual DateTime? FailedPasswordAttemptWindowStart { get; set; }
public virtual int? FailedPasswordAnswerAttemptCount { get; set; }
public virtual DateTime? FailedPasswordAnswerAttemptWindowStart { get; set; }
public virtual string Comment { get; set; }
public virtual IList<Roles> RoleList { get; set; }
public virtual Profiles Profiles { get; set; }
}
}
using System;
using System.Collections.Generic;
namespace Custom.NHibernateLib.Model
{
public class Roles : Entity
{
public Roles(){}
public virtual long RoleId { get; set; }
public virtual string ApplicationName { get; set; }
public virtual string RoleName { get; set; }
public virtual IList<Users> UserList { get; set; }
}
}
Adding the user and the role to their respective collections.
usr.RoleList.Add(role);
role.UserList.Add(usr);
When this is called session.SaveOrUpdate(role) then this occurs in NHibernate.
-- statement #1
UPDATE Users...WHERE UserId = 32768
-- statement #2
UPDATE Roles...WHERE RoleId = 65536
-- statement #3
INSERT INTO UserRoles...
Ok well I can conclude the my issue was caused by the way I was handling the session and commit after the save and update. I was closing the session then recreating it within multiple methods in my custom membership library code. For example, each method I was calling I was wrapping it around a using statement for the session and transaction. I should have known better and not go by my assumptions and just spend the time to RTM.
The NHibernate in Action book about session management and using current session context is what guided me. I coded that up to use in my unit test and everything worked fine. Though the book is a bit dated it still had some good basic info.
Problem encountered
When I create a transient instance with a children collection, everything gets persisted.
Aside, if I update an instance of one of the children object, it doesn't get updated when I save the parent object.
I'm actually using cascade="all"
Problem reproduction
The problem occurs when I have loaded all of my Customer occurences, and I change an invoice, though I always use the same ISession.
var repository = new CustomerRepository(session);
var customers = repository.GetAll();
var customer = customers.Where(c => c.Name == "Stack Overflow").FirstOrDefault();
customer.Invoices
.Where(i => i.Number == "1234")
.Approve(WindowsIdentity.GetCurrent().Name);
repository.Save(customer);
Step by step debugging clearly shows the repository.Save() method being executed, and the changes won't show inte the underlying database.
An update directly against the database table is possible, so no contraint causing the update to fail on the database-side.
Herewith some code in case it might help.
Customer.hbm.xml
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="MyProject.Model"
assembly="MyProject">
<class name="Customer" table="AC_CUST" schema="AC">
<id name="Id" column="AC_CUST_ID" type="Int32" unsaved-value="0">
<generator class="sequence-identity">
<param name="sequence">AC_CUST_ID_SEQ</param>
<param name="schema">AC</param>
</generator>
</id>
<property name="Name" column="AC_CUST_NAME" type="String"
not-null="true" />
<property name="PhoneNumber" column="AC_CUST_PHNUM" type="Int64"
not-null="true" />
<bag name="Invoices" table="ESO_RAPP_ACCES_INFO_DSQ" schema="AC"
fetch="join" lazy="true" inverse="true" cascade="all">
<key column="AC_CUST_ID" foreign-key="AC_CUST_INV_FK" />
<one-to-many class="Invoice" />
</bag>
</class>
</hibernate-mapping>
Customer
public class Customer {
public Customer() { Invoices = new List<Invoice>(); }
public virtual int Id { get; proected set; }
public virtual IList<Invoice> Invoices { get; protected set; }
public virtual string Name { get; set; }
public virtual string PhoneNumber { get; set; }
}
Invoice.hbm.xml
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="MyProject.Model"
assembly="MyProject">
<class name="Invoice" table="AC_INV" schema="AC">
<id name="Id" column="AC_INV_ID" type="Int32" unsaved-value="0">
<generator class="sequence-identity">
<param name="sequence">AC_INV_ID_SEQ</param>
<param name="schema">AC</param>
</generator>
</id>
<property name="Approved" column="AC_INV_APPRD" type="DateTime"
not-null="false" />
<property name="Approver" column="AC_INV_APPRR" type="String" length="15"
not-null="false" />
<property name="Number" column="AC_INV_NUMBR" type="String" length="15"
not-null="true" />
<property name="Produced" column="AC_INV_PROD" type="DateTime"
not-null="false" />
<many-to-one class="Customer" column="AC_CUST_ID" />
</class>
</hibernate-mapping>
Invoice
public class Invoice {
public Invoice() {
Items = new List<Item>();
Produced = DateTime.Now;
}
public virtual DateTime? Approved { get; protected set; }
public virtual string Approver { get; protected set; }
public virtual Customer Customer { get; set; }
public virtual int Id { get; proected set; }
public virtual string Number { get; set; }
public virtual DateTime? Produced { get; set; }
public virtual void Approve(string approver) {
Approved = DateTime.Now;
Approver = approver;
}
public virtual void Reject() { Produced = null; }
}
CustomerRepository
public class CustomerRepository {
public CustomerRepository(ISession session) { Session = session; }
public ISession Session { get; protected set; }
public Customer Save(Customer instance) {
Session.SaveOrUpdate(instance);
return Instance;
}
}
Related articles
nHibernate one-to-many inserts but doesnt update
NHibernate - code example for update
Any help appreciated.
Don't forget to Flush() your session!
NHibernate keeps track of all the changes along a session lifecycle which should only live for a form (Desktop) or a page (Web).
Because of the session is aware of all the changes, it doesn't necessarily commit the changes to the underlying database. Instead, it keeps a record of all the changes into a dictionary, then when it is flushed by calling ISession.Flush(), you actually demand the session to commit the changes for good.
So solution shall be:
repository.Save(customer);
session.Flush();
Or you may as well code a Commit() method within your repository which would Flush() the session upon a call.
repository.Save(customer);
repository.Commit();
And your repository would look like:
// Assuming you stock your session in the `Session` property.
public void Commit() { Session.Flush(); }
That's all!
I am having problems deleting an entity from the database using NHibernate v3.
foreach (ShoppingCart s in query )
{
_session.Delete(s);
trans.Commit();
break;
}
"The given key was not present in the dictionary"
There really isn't any more specific information than that. Basically, I'm using two entities as shown below:
| ShoppingCart |==========> | Product |
ShoppingCart PK: ShoppingCartID, ProductID (also is FK)
Product PK: ProductID (no FK)
... A ShoppingCart entity which has many Products inside it.
My ShoppingCart mapping entity look like this:
<class name="ShoppingCart" table="LC_SHOPPINGCART" lazy="true" >
<composite-id>
<key-property name="CartID" column="CARTID" />
<key-property name="ProductID" column="PRODUCTID" />
<key-property name="Size" column="SIZE" />
<key-property name="Color" column="COLOR" />
</composite-id>
<many-to-one foreign-key="ProductID" insert="false" update="false" lazy="false" name="Product">
<column name="PRODUCTID" sql-type="INT" not-null="true" />
</many-to-one>
<property name="Quantity">
<column name="QUANTITY" sql-type="INT" not-null="false" />
</property>
<property name="DateAdded">
<column name="DATEADDED" sql-type="SMALLDATETIME" not-null="false" />
</property>
<property name="Price">
<column name="PRICE" sql-type="MONEY" not-null="false" />
</property>
And the Entity class for ShoppingCart:
public ShoppingCart() { }
[Key]
public virtual string CartID { get; set; }
public virtual int ProductID { get; set; }
public virtual string Size { get; set; }
public virtual string Color { get; set; }
public virtual Product Product { get; set; }
public virtual System.Nullable<int> Quantity { get; set; }
public virtual DateTime DateAdded { get; set; }
public virtual System.Nullable<decimal> Price { get; set; }
My Product Mapping looks like this:
<class name="Product" table="LC_PRODUCT" lazy="true" >
<id name="ProductID" column="ProductID">
<generator class="native"></generator>
</id>
<list name="ShoppingCarts"
cascade="all-delete-orphan">
<key column="ProductID"></key>
<index column="CartID"></index>
<one-to-many class="ShoppingCart"></one-to-many>
</list>
<property name="Name">
<column name="NAME" sql-type="NVARCHAR" not-null="false" />
</property>
<property name="Description">
<column name="DESCRIPTION" sql-type="VARCHAR" not-null="false" />
</property>
<property name="Price">
<column name="PRICE" sql-type="MONEY" not-null="false" />
</property>
<property name="Image1FileName">
<column name="IMAGE1FILENAME" sql-type="NVARCHAR" not-null="false" />
</property>
<property name="Image2FileName">
<column name="IMAGE2FILENAME" sql-type="NVARCHAR" not-null="false" />
</property>
<property name="OnCatalogPromotion">
<column name="ONCATALOGPROMOTION" sql-type="BIT" not-null="false" />
</property>
<property name="OnDepartmentPromotion">
<column name="ONDEPARTMENTPROMOTION" sql-type="BIT" not-null="false" />
</property>
<property name="Sizeable">
<column name="SIZEABLE" sql-type="BIT" not-null="false" />
</property>
<property name="Colorable">
<column name="COLORABLE" sql-type="BIT" not-null="false" />
</property>
<property name="DateAdded">
<column name="DATEADDED" sql-type="SMALLDATETIME" not-null="false" />
</property>
<property name="TotalRemaining">
<column name="TOTALREMAINING" sql-type="INT" not-null="false" />
</property>
</class>
...And the corresponding Product entity class:
public Product() { }
[Key]
public virtual int ProductID { get; set; }
public virtual IList<ShoppingCart> ShoppingCarts { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual System.Nullable<decimal> Price { get; set; }
public virtual string Image1FileName { get; set; }
public virtual string Image2FileName { get; set; }
public virtual bool OnCatalogPromotion { get; set; }
public virtual bool OnDepartmentPromotion { get; set; }
public virtual bool Sizeable { get; set; }
public virtual bool Colorable { get; set; }
public virtual DateTime DateAdded { get; set; }
public virtual System.Nullable<int> TotalRemaining { get; set; }
The stack trace is provided as follows:
Server Error in '/ElectronicsRob' Application.
The given key was not present in the dictionary.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
Source Error:
Line 106: //db.lc_ShoppingCarts.DeleteOnSubmit(query);
Line 107: //db.SubmitChanges();
Line 108: new ShoppingCartRepository().DeleteProductFromCart( System.Convert.ToInt32 (Grid1.SelectedDataKey["ProductID"]), LinqShoppingCartAccess.cartID);
Line 109: PopulateControls();
Line 110: }
Source File: c:\inetpub\wwwroot\ElectronicsRob\LINQControls\ControlTemplates\LinqShoppingCart.ascx.cs
Line: 108
Stack Trace:
[KeyNotFoundException: The given key was not present in the dictionary.]
NHibernate.Engine.StatefulPersistenceContext.RemoveEntity(EntityKey key) in d:\CSharp\NH\nhibernate\src\NHibernate\Engine\StatefulPersistenceContext.cs:435
NHibernate.Action.EntityDeleteAction.Execute() in d:\CSharp\NH\nhibernate\src\NHibernate\Action\EntityDeleteAction.cs:88
NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in d:\CSharp\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:136
NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in d:\CSharp\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:125
NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:241
NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs:20
NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:1477
NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:190
ObjexxRepositories.ShoppingCartRepository.DeleteProductFromCart(Nullable`1 prodID, String cartID) in C:\Users\Rob\Documents\Visual Studio 2010\Projects\NGC6744\ObjexxRepositories\Concrete\ShoppingCartRepository.cs:78
LINQControls_LinqShoppingCart.Grid1_RowDeleting(Object sender, GridViewDeleteEventArgs e) in c:\inetpub\wwwroot\ElectronicsRob\LINQControls\ControlTemplates\LinqShoppingCart.ascx.cs:108
System.Web.UI.WebControls.GridView.HandleDelete(GridViewRow row, Int32 rowIndex) +814
System.Web.UI.WebControls.GridView.HandleEvent(EventArgs e, Boolean causesValidation, String validationGroup) +431
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3707
Any help is appreciated, thank you!
I have two classes:
namespace fm.web
{
public class User
{
public static string default_username = "guest";
public static string default_password = "guest";
private UserType usertype;
public virtual int? Id { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual DateTime Datecreated { get; set; }
public virtual string Firstname { get; set; }
public virtual string Lastname { get; set; }
public virtual string Email { get; set; }
public virtual UserType Usertype
{
get { return usertype; }
set { usertype = value; }
}
}
}
namespace fm.web
{
public class UserType
{
public virtual int? Id { get; set; }
public virtual string Title { get; set; }
}
}
Here are the mapping files
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="fm.web"
assembly="fm.web">
<class name="User" table="[user]">
<id name="Id">
<column name="id" />
<generator class="native" />
</id>
<property name="Username" />
<property name="Password" />
<property name="Datecreated" />
<many-to-one name="Usertype"
class="UserType"
column="[type]"
cascade="all"
lazy="false"
/>
<property name="Firstname" />
<property name="Lastname" />
<property name="Email" />
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="fm.web"
assembly="fm.web">
<class name="UserType" table="[user_type]">
<id name="Id">
<column name="id" />
<generator class="native" />
</id>
<property name="Title" />
</class>
</hibernate-mapping>
I'm getting an exception: DuplicateMappingException
Could not compile the mapping document: fm.web.data.User.hbm.xml
Duplicate class/entity mapping User
Is nhibernate always this hard? Maybe I need a different framework.
I really think the mappings are fine which leads me to believe that the configuration setup is not quite right.
Please can you check that BuildSessionFactory is only called once on application start up.
Also please check that you are not including the mapping files twice as this will also throw this type of error.
Please post your configuration code.
You are correct in thinking that NHibernate is difficult to grasp for new comers espically the session management and mappings. Once you have grasped this then things get easier and are well worth the effort.
I am getting "illegal access to loading collection" exception while trying to populate "IList" property in Supplier Domain using NHibernate . I have tried all suggestions I got by googling but nothing seems to help :(
Here are my domain objects and .HBM files. I would greatly appreciate your help/suggestions.
Supplier Domain Object
namespace Inventory.DomainObjects
{
[Serializable]
public class Supplier
{
public virtual string SupplierID { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual IList<Address> Address { get; set; }
}
}
Address Domain Object
namespace Inventory.DomainObjects
{
[Serializable]
public class Address
{
public virtual int AddressID { get; set; }
public virtual string SupplierID { get; set; }
public virtual string Line1 { get; set; }
public virtual string Line2 { get; set; }
public virtual string Line3 { get; set; }
}
}
Supplier.HBM
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Inventory.DomainObjects"
assembly="Inventory">
<class name="Supplier" table="Inv_Supplier">
<id name="SupplierID" column="SupplierId" type="string"/>
<property name="SupplierCode" column="Code" type="string"/>
<property name="Name" column="SupplierName" type="string"/>
<property name="Description" column="SupplierDescription" type="string"/>
<bag name="Address" cascade="all" inverse="true" lazy="true">
<key column="SupplierID" not-null="true"/>
<one-to-many class="Address" not-found="ignore"/>
</bag>
</class>
</hibernate-mapping>
Address.HBM
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Inventory.DomainObjects"
assembly="Inventory">
<class name="Address" table="Inv_Supplier_Address" lazy="false">
<id name="AddressID" column="AddressId" type="integer"/>
<property name="Line1" column="Line1" type="string"/>
<property name="Line2" column="Line2" type="string"/>
<property name="Line3" column="Line3" type="string"/>
<many-to-one name="SupplierID" column="SupplierId" not-null="true" class="Supplier" />
</class>
</hibernate-mapping>
This looks suspicious:
<many-to-one name="SupplierID" column="SupplierId"
not-null="true" class="Supplier" />
Could you try removing the above line to see if the problem goes away?
If this fixes the problem you should add the many-to-one back as follows:
namespace Inventory.DomainObjects
{
[Serializable]
public class Address
{
public virtual int AddressID { get; set; }
// CHANGED: reference supplier object instead of ID
public virtual Supplier Supplier { get; set; }
public virtual string Line1 { get; set; }
public virtual string Line2 { get; set; }
public virtual string Line3 { get; set; }
}
}
then change your hbm mapping file like this (to reference the Supplier property instead of SupplierId
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Inventory.DomainObjects"
assembly="Inventory">
<class name="Address" table="Inv_Supplier_Address" lazy="false">
<id name="AddressID" column="AddressId" type="integer"/>
<property name="Line1" column="Line1" type="string"/>
<property name="Line2" column="Line2" type="string"/>
<property name="Line3" column="Line3" type="string"/>
<many-to-one name="Supplier" column="SupplierId"
not-null="true" class="Supplier" />
</class>
</hibernate-mapping>