Different results using IQuery and Repository in Web App - nhibernate

Hope someone can point me in the right direction. I'm just starting out with nHibernate and a little confused over this one. It's running within a .Net Web Application.
Basically I've got 2 classes - Coupon and Publisher.
As a test, and to make sure NH was set up correctly, I accessed the PublisherRepository and pulled a publisher out by it's name. That works fine and reports success.
IPublisherRepository repo = new PublisherRepository();
Response.Write(repo.GetByName("Publisher 5"));
As a second test, I then used the CreateQuery method to get all of the Publishers, like so:
IQuery query = session.CreateQuery("from CartManData.Domain.Publisher pub");
This returns no data - the list is empty. Same goes using Linq:
session.Query<Publisher>().Where(x=>x.Name == "Publisher 4").ToList<Publisher>()
Using Sql Profiler I can see that the first test hits the database, and retrieves the set (called Coupons belonging to a Publisher) as lazy loading is off. However, the second 2 approaches don't hit the database at all - and I'm stumped as to why.
Here's the mapping file for Publisher and Coupon. They're embedded and I know they're working otherwise the repo wouldn't work either :
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Publisher" lazy="false">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="AddressLine1"></property>
<property name="AddressLine2"></property>
<property name="AddressLine3"></property>
<property name="Town"></property>
<property name="PostCode"></property>
<property name="Telephone"></property>
<property name="Email"></property>
<property name="Enabled"></property>
<property name="CommissionRate"></property>
<set name="Coupons" cascade="none" lazy="false">
<key column="PublisherId" ></key>
<one-to-many class="Coupon" />
</set>
</class>
</hibernate-mapping>
And Coupons:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Coupon">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="EffectiveFrom"></property>
<property name="EffectiveTo"></property>
<property name="UnitPrice"></property>
<property name="OriginalPrice"></property>
<property name="CouponImage"></property>
<property name="Enabled"></property>
<property name="PublisherId" not-null="false"></property>
</class>
</hibernate-mapping>
Any help on this really appreciate - sure it's something I've missed.
Cheers,
Tony
Additional Info
The Session object is retrieved via a HttpModule, where the session object is bound to CurrentSessionContext. That seems to be working fine as if you check to see if session is open, it reports it is.
PublisherRepository.GetByName() looks like this:
using (ISession session = NHibernateHelper.OpenSession())
{
return session.CreateCriteria(typeof(Publisher))
.Add(NHibernate.Criterion.Restrictions.Eq("Name", name))
.UniqueResult<Publisher>();
}
Log4Net Output
During the call via CreateQuery (the 2nd example above), this is what NHibernate is reporting:
2012-08-22 16:22:28,075 [15] DEBUG rollingFile - START of retrieval
2012-08-22 16:22:28,081 [15] DEBUG NHibernate.Engine.Query.QueryPlanCache - unable to locate HQL query plan in cache; generating (from CartManData.Domain.Publisher pub)
2012-08-22 16:22:28,128 [15] DEBUG NHibernate.Hql.Ast.ANTLR.HqlParseEngine - parse() - HQL: from CartManData.Domain.Publisher pub
2012-08-22 16:22:28,174 [15] DEBUG NHibernate.Hql.Ast.ANTLR.ErrorCounter - throwQueryException() : no errors
2012-08-22 16:22:28,200 [15] DEBUG NHibernate.Engine.Query.QueryPlanCache - unable to locate HQL query plan in cache; generating (from CartManData.Domain.Publisher pub)
2012-08-22 16:22:28,201 [15] DEBUG NHibernate.Hql.Ast.ANTLR.HqlParseEngine - parse() - HQL: from CartManData.Domain.Publisher pub
2012-08-22 16:22:28,202 [15] DEBUG NHibernate.Hql.Ast.ANTLR.ErrorCounter - throwQueryException() : no errors
2012-08-22 16:22:28,206 [15] DEBUG NHibernate.Engine.Query.HQLQueryPlan - enumerable: from CartManData.Domain.Publisher pub
2012-08-22 16:22:28,208 [15] DEBUG NHibernate.Engine.QueryParameters - named parameters: {}
2012-08-22 16:22:28,210 [15] DEBUG rollingFile - End of retrieval

You seem to have problems with your mappings.
I've tried to recreate your situation and I have changed your hbm files a little bit
Publisher
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Publisher" lazy="false">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="AddressLine1"></property>
<property name="AddressLine2"></property>
<property name="AddressLine3"></property>
<property name="Town"></property>
<property name="PostCode"></property>
<property name="Telephone"></property>
<property name="Email"></property>
<property name="Enabled"></property>
<property name="CommissionRate"></property>
<set name="Coupons" cascade="all-delete-orphan" inverse="true" lazy="false">
<key column="PublisherId" />
<one-to-many class="CartManData.Domain.Coupon, CartManData" />
</set>
</class>
</hibernate-mapping>
Coupon
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Coupon">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="EffectiveFrom"></property>
<property name="EffectiveTo"></property>
<property name="UnitPrice"></property>
<property name="OriginalPrice"></property>
<property name="CouponImage"></property>
<property name="Enabled"></property>
<many-to-one class="CartManData.Domain.Publisher, CartManData" name="Publisher">
<column name="PublisherId" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
As you can see I've used a many-to-one relationship on Coupon.
You can read how these relationships work here
I've used the inverse="true" in the set defined for the Publisher mapping. Some more information about inverse.
If you want to have a look at the 2 classes:
public class Publisher
{
public Publisher()
{
this.Coupons = new HashSet<Coupon>();
}
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string AddressLine1 { get; set; }
public virtual string AddressLine2 { get; set; }
public virtual string AddressLine3 { get; set; }
public virtual string Town { get; set; }
public virtual string PostCode { get; set; }
public virtual string Telephone { get; set; }
public virtual string Email { get; set; }
public virtual bool Enabled { get; set; }
public virtual decimal CommissionRate { get; set; }
public virtual ICollection<Coupon> Coupons { get; set; }
}
and
public class Coupon
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime EffectiveFrom { get; set; }
public virtual DateTime EffectiveTo { get; set; }
public virtual double UnitPrice { get; set; }
public virtual double OriginalPrice { get; set; }
public virtual string CouponImage { get; set; }
public virtual bool Enabled { get; set; }
public virtual Publisher Publisher { get; set; }
}
You can download a working example (NHVariousTests) here.

Related

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.

Dictionary won't persist with NHibernate 3.0

I have the following entity:
public class Alert
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IDictionary<CxChannel, string> Messages { get; set; }
}
public enum CxChannel
{
Message,
Email
}
and following mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Entities.Alert, Entities" table="alert">
<id name="Id" type="int" unsaved-value="0" access="property">
<generator class="identity"/>
</id>
<property name="Name" column="name"/>
<map name="Messages" table="alert_message" cascade="all">
<key column="alert_id"/>
<index column="channel" type="Entities.CxChannel, Entities"/>
<element column="message" type="System.String"/>
</map>
</class>
</hibernate-mapping>
The problem is that when I save an alert entity, Messages dictionary is not persisted to database. As a matter of fact my code looks like the code Oren used in his blog post: http://ayende.com/Blog/archive/2009/06/03/nhibernate-mapping-ndash-ltmapgt.aspx
Has anyone experienced same issue?
Verify that your channel column is an integer in your schema as the CxChannel enum will be mapped as 0 (for Message) and 1 (for email). I just pasted your code and mappings into a console project, used new SchemaExport(cfg).Execute(false, true, false), and successfully inserted rows into the generated database.

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.

NHibernate property formula filter

I have a following class:
MyClass
public virtual int Id { get; set; }
public virtual int Code { get; set; }
public virtual int Description { get; set; }
public virtual int Name { get; set; }
with the following mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="TestApplication" assembly="TestApplication">
<class name="MyClass" table="MyTable">
<id name="Id" column="id">
<generator class="native"/>
</id>
<property name="Code" column="code"/>
<property name="Description" column="description"/>
<property name="Name" formula="(SELECT b.translation FROM translations b WHERE b.translation_id = translation_id AND b.language_id = :TranslationFilter.LanguageId)"/>
</class>
<filter-def name="TranslationFilter">
<filter-param name="LanguageId" type="Int32"/>
</filter-def>
</hibernate-mapping>
I'm trying to load entity through spring with:
Session.EnableFilter("TranslationFilter").SetParameter("LanguageId", 1);
return Session.Get<MyClass>(1);
but I'am getting adoexception. I see (in a profiler) that variable :TranslationFilter.LanguageId is not replaced with ? and that parameter value is not send to the server?
Is it this possible (to have filters in formula) and how?
Many thanks!
This feature is not officially supported. As such oren's blog post about this combination of 2 different features (formulas and filters) should be taken with a grain of salt...