NHibernate property formula filter - nhibernate

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...

Related

Different results using IQuery and Repository in Web App

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.

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.

Is it possible to use an NHibernate type discriminator as part of a foreign key

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.

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.