nhibernate weird DuplicateMappingException - nhibernate

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.

Related

Multiple update statements issued before an the insert statement in NHibernate on a many to many relationship

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.

cascade="all" or cascade="save-update"? NHibernate one-to-many won't update

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!

nhibernate <bag> exception - illegal access to loading collection

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>

Is my NHibernate book wrong?

I'm starting to learn NHibernate (3.0) and picked up a copy of Packt Publishing's NHibernate 3.0 Cookbook.
There's a section on one-to-many mappings which I'm walking through but with my own database. It suggests I should do something like this to model a one to many relationship between customers and their domains:
public class Customer
{
public virtual int Id { get; protected set; }
public virtual string CustomerName { get; set; }
// Customer has many domains
public virtual IList<Domain> Domains { get; set; }
}
public class Domain
{
public virtual int Id { get; protected set; }
public virtual int CustomerID { get; set; }
public virtual string DomainName { get; set; }
}
Customer Mapping:
<class name="Customer" table="tblCustomer">
<id name="Id">
<column name="CustomerID" sql-type="int" not-null="true"/>
<generator class="identity"/>
</id>
<property name="CustomerName" column="Customer"/>
<list name="Domains">
<key column="CustomerID"/>
<one-to-many class="Domain" />
</list>
</class>
When I run this I get the following error:
XML validation error: The element 'list' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'one-to-many' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'index, list-index' in namespace 'urn:nhibernate-mapping-2.2'.
The book's example is a bit more complex in that they use table-per-subclass mappings:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Eg.Core"
namespace="Eg.Core">
<subclass name="Movie" extends="Product">
<property name="Director" />
<list name="Actors" cascade="all-delete-orphan">
<key column="MovieId" />
<index column="ActorIndex" />
<one-to-many class="ActorRole"/> <-- Is this wrong?
</list>
</subclass>
</hibernate-mapping>
I'm guessing the book is wrong?
No, your mapping is missing the index element. A list in NHibernate is an ordered set, if you want an unordered set use bag mapping.

NHibernate property mapping: columns and formula

When i map columns from the inspected table, i do this:
<property name="InstanceName" type="MyNameUserType, MyApp.MyNamespace">
<column name="Name"/>
<column name="Name2"/>
</property>
How can I make property mapping initialize a UserType with data retrieved by the formula's sql query?
<property name="InstanceName" type="MyNameUserType, MyApp.MyNamespace" formula="(...)"/>
fails with an exception "wrong number of columns".
Thanks in advance!
MyUserNameType should be a class level mapping so that you can map the result of the SQL function to a class. See these two posts for some possible help:
Class and SQL Function example: http://thoughtspam.spaces.live.com/blog/cns!253515AE06513617!478.entry
NHibernate Mapping with formula mapping example:
http://thoughtspam.spaces.live.com/blog/cns!253515AE06513617!477.entry
I'm the author of the articles referenced by Michael. I had no idea people where still interested and I'm not sure it's applicable with the latest NHibernate.
Here's a fresh link though: http://thoughtspam.wordpress.com/2007/12/19/nhibernate-property-with-formula/
example, using Northwind...
Mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="PropertyFormulaExample.Shipper, PropertyFormulaExample" table="Shippers" lazy="false" >
<id name="ShipperID" column="ShipperID" unsaved-value="0">
<generator class="native" />
</id>
<property name="CompanyName" column="CompanyName" />
<property name="Phone" column="Phone" />
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="PropertyFormulaExample.Order, PropertyFormulaExample" table="Orders" lazy="false">
<id name="OrderID" column="OrderID" unsaved-value="0">
<generator class="native" />
</id>
<property name="CustomerID" column="CustomerID" />
<property name="ShipVia" type="PropertyFormulaExample.Shipper, PropertyFormulaExample" formula="dbo.GetShipper(shipvia)" />
</class>
</hibernate-mapping>
Entities:
public class Order
{
public int OrderID { get; set; }
public string CustomerID { get; set; }
public Shipper ShipVia { get; set; }
}
public class Shipper : ILifecycle
{
public int ShipperID { get; set; }
public string CompanyName { get; set; }
public string Phone { get; set; }
#region ILifecycle Members
public LifecycleVeto OnDelete(NHibernate.ISession s)
{
throw new NotImplementedException();
}
public void OnLoad(NHibernate.ISession s, object id)
{
}
public LifecycleVeto OnSave(NHibernate.ISession s)
{
throw new NotImplementedException();
}
public LifecycleVeto OnUpdate(NHibernate.ISession s)
{
throw new NotImplementedException();
}
#endregion
}
And finally the SQL function:
CREATE FUNCTION dbo.GetShipper(#shipperId int)
RETURNS int
AS
BEGIN
RETURN #shipperId
END
Obviously, you’ll want the function to do something meaningful, but the idea is you return the PK for the entity and implement ILifecycle.