I use Criteria Querying. I created a simple helper that creates subcriteria when I need to filter the query with properties inside properies (such as Person.Address.Street = :streetName), that's working fine. My problem comes when I try to order the results with those inner properties (even if I'm not actually filtering the results by them).
Right now, what I'm trying to do is:
My site accepts Posts, which people use to sell stuff. Those posts can be highlighted (it's another object) and Highlights have a Price (type: decimal). So I wish to select all Post that match a criteria, and then order them by the Price of the Highlight. The filtering works fine (I don't use the Highlight for filtering) but when I try to order the results I get an error:
Ordering:
Code:
criteria.AddOrder(new NHibernate.Expression.Order(itemOrder.Property, true)); //where the value for itemOrder.Property is "Highlight.Price"
Error:
could not resolve property: Highlight.Price of :Rules.Post where "Rules" is the namespace of all my business rules classes.
So I searched and red that I need to set an alias for "Highlight". I did so, and changed my helper: when in the order by I have a "." it means I have an inner property, so I have to create the alias:
(this is replacing general code with specific code for this example)
Code:
criteria.CreateAlias("Highlight", "_Alias_Highlight");
itemOrder.Property = "_Alias_Highlight.Price"; //former value was "Highlight.Price"
criteria.AddOrder(new NHibernate.Expression.Order(itemOrder.Property, true));
And now the error says
could not resolve property: _Alias_Highlight.Price of :Rules.Postpretty much the same message except now it says _Alias_Highlight en stead of Highlight.
This is the problem I have, if you think you can solve it or need extra info, post me an answer and you can stop reading here...
Now I'll explain what I see when I step into NHibernate code when sorting Criteria, maybe the answer to my problem may be in here... also, in a comment inside the code I explain where there MAY be a bug.
Code in CriteriaLoader.cs, folder: Loader
Code:
public CriteriaLoader( IOuterJoinLoadable persister, ISessionFactoryImplementor factory, CriteriaImpl criteria )
: base( persister, factory )
{
this.criteria = criteria;
//deleted for clarity: code for creating sql including the WHERE clause
StringBuilder orderBy = new StringBuilder( 30 );
foreach( Order ord in criteria.IterateOrderings() )
{
orderBy.Append( ord.ToSqlString( factory, criteria.CriteriaClass, Alias ) );
}
//deleted for clarity: some other code
}
when the line
orderBy.Append( ord.ToSqlString( factory, criteria.CriteriaClass, Alias ) );
is called, criteria.CriteriaClass's value is Rules.Post, and Alias's value is "this". The next piece of code is executed (in Order.cs, folder: Expression):
Code:
public string ToSqlString( ISessionFactoryImplementor sessionFactory, System.Type persistentClass, string alias )
{
string[ ] columns = AbstractCriterion.GetColumns( sessionFactory, persistentClass, _propertyName, alias, emptyMap );
//deleted for clarity: a check and some other code
}
the problem is that the variable "emptyMap" has no mappings (I guess it's name is pretty clear here!), but it is used in AbstractCriterion.GetColumns to try to find the mappings (AbstractCriterion.cs, folder: Expression)
Code:
protected internal static string[ ] GetColumns( ISessionFactoryImplementor factory, System.Type persistentClass, string property, string alias, IDictionary aliasClasses )
//note: the value of the parameters are:
//persistentClass = Rules.Post (a System.Type)
//property = _Alias_Highlight.Price
//alias = this
//aliasClasses = the empty mapping IDictionary previously mentioned
{
if( property.IndexOf( '.' ) > 0 ) //note: this is true
{
string root = StringHelper.Root( property ); //note: root's new value is _Alias_Highlight
System.Type clazz = aliasClasses[ root ] as System.Type; //note: clazz is null, because aliasClasses is empty. If it was Highlight, then the following code would be executed and the ordering would work fine. Could this be a bug?
if( clazz != null )
{
persistentClass = clazz;
alias = root;
property = property.Substring( root.Length + 1 );
}
}
return GetPropertyMapping( persistentClass, factory ).ToColumns( alias, property );
}
I hope you are not snoring at this point... anyway, as I say in the comment I added inside the code, I think that if aliasClasses had a value, the ordering would work perfectly...
Well? Any ideas?