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>
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.
This is my domain class:
public class User
{
public Guid id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string mailAddress { get; set; }
}
This is my mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Test" namespace="Test.model">
<class name="User" >
<id name="id">
<generator class="guid"/>
</id>
<property name="firstName" />
<property name="lastName" />
<property name="mailAddress" />
</class>
</hibernate-mapping>
This is My hibernate-cfg file:
<?xml version="1.0" encoding="utf-8" ?>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MySQLDialect</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">Server=localhost;Database=vbook;User ID=root;Password=ziben</property>
<!--<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>-->
<property name="hbm2ddl.auto">update</property>
<property name="generate_statistics">true</property>
<property name="show_sql">true</property>
<mapping file="data/mapping/User.hbm.xml" />
This is how I create session factory:
var configuration = new Configuration();
configuration.Configure();
_sessionFactory = configuration.BuildSessionFactory();
I get the following exception:
Could not compile the mapping document: data/mapping/User.hbm.xml
I can't understand why.
Help please.
Any C# property of your entiy must be declared as virtual. Change your class this way:
public class User
{
public virtual Guid id { get; set; }
public virtual string firstName { get; set; }
public virtual string lastName { get; set; }
public virtual string mailAddress { get; set; }
}
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'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.
Taking the following object model:
public abstract class Entity
{
public Guid Id { get; set; }
}
public class Category : Entity
{
public string Name { get; set; }
public ICollection<LocalizedProperty> LocalizedProperties { get; set; }
}
public class Product : Entity
{
public string Name { get; set; }
public ICollection<LocalizedProperty> LocalizedProperties { get; set; }
}
public class LocalizedProperty : Entity
{
public string CultureName { get; set; }
public string PropertyName { get; set; }
public string PropertyValue { get; set; }
}
Is it possible to use a type discriminator along with the entity's Id as the foreign key. The idea is that the resultant LocalizedProperties table would be:
LocalizedProperties
-------------------
Id
EntityType
EntityId
CultureName
PropertyName
PropertyValue
I know this is possible using Table-per-subclass mapping where each of my "Localized" entities inherit from a base localized entity class, which in turn has the association with LocalizedProperty. However, I would rather not have this extra level of inheritance if the above is possible.
Thanks,
Ben
UPDATE
Thanks to Diego for providing the solution using confORM. For those of you using traditional mapping files, I have converted the example from http://fabiomaulo.blogspot.com/2010/11/conform-any-to-many.html
<?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="ConfOrm.UsageExamples.CreateXmlMappingsInBinFolder" assembly="ConfOrm.UsageExamples" xmlns="urn:nhibernate-mapping-2.2">
<class name="Blog">
<id name="Id" type="Guid">
<generator class="guid.comb" />
</id>
<property name="Title" />
<property name="Subtitle" />
<set name="Tags" cascade="all" where="TagedItemClass = 'ConfOrm.UsageExamples.CreateXmlMappingsInBinFolder.Blog'">
<key column="TagedItemId" foreign-key="none" />
<one-to-many class="Tag" />
</set>
</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="ConfOrm.UsageExamples.CreateXmlMappingsInBinFolder" assembly="ConfOrm.UsageExamples" xmlns="urn:nhibernate-mapping-2.2">
<class name="Tag">
<id name="Id" type="Guid">
<generator class="guid.comb" />
</id>
<property name="Name" />
<any id-type="Guid" name="TagedItem">
<column name="TagedItemClass" />
<column name="TagedItemId" />
</any>
</class>
</hibernate-mapping>
You can use <any>.
http://nhibernate.info/doc/nh/en/index.html#mapping-types-anymapping
For a full example, check http://fabiomaulo.blogspot.com/2010/11/conform-any-to-many.html. I think it's exactly what you need.