HQL: Querying dynamic-component property - nhibernate

If I have a mapping like this:
<class name="Library" table="Libraries">
...
<dynamic-component name="Annotations">
<property name="LibraryResolver.AlgorithmVersion" column="`LibraryResolver.AlgorithmVersion`" type="Int32" />
</dynamic-component>
</class>
How should I write HQL or Linq-to-NHibernate query for all libraries where LibraryResolver.AlgorithmVersion is greater than a given value?

The HQL query below maybe along the lines you are looking for
from Library as lib
where lib.Annotations.LibraryResolver.AlgorithmVersion > 2
If you're using nhibernate, have you tried out the NHibernate LambdaExtensions? This library provides a set of extensions methods over the Criteria and DetachedCriteria apis which removes the need of magic strings when querying using the above two api.
Below is an example of how one might use NHibernate Detached Criteria query with the mentioned LambdaExtensions library
Answer answerAlias = null;
var actual = DetachedCriteria.For<Survey>()
.Add<Survey>( s => s.Status == SurveyStatus.Complete )
.Add<Questionnaire>( q => q.Id == questionnaireId )
.CreateAlias<Survey>( s => s.Answers, () => answerAlias )
.SetProjection( LambdaProjection.Property( () => answerAlias.Id ) );

I don't know whether this helps but when I use the Criteria API (in Java) it just works. Haven't tried with HQL though.
<dynamic-component name="values">
<property name="dynamicNameValue" column="ATTRIBUTE_1" type="string"/>
<property name="dynamicNumber" column="ATTRIBUTE_4" type="integer"/>
</dynamic-component>
Criteria criteria = session.createCriteria(DynamicAttributes.class)
.add(Expression.eq("values.dynamicNumber", 2));
Just some thought: could it be that the problem is that the name ('LibraryResolver.AlgorithmVersion') you're passing contains a dot? Maybe post some code you already attempted?

Related

Fluent NHibernate Complex Composite Key Mapping

I'm afraid to even ask this question really, as I find the whole thing pretty disgusting myself. But, what are you going to do with a legacy database.
I have the following three tables
Generator Alarm AlarmDescription
--------- ----- ----------------
Id Id
Id <- GensetId DescriptionText
EventTypeId -> AlarmCode
PanelId ----------------> PanelId
If it's not clear from the above rendering, I have an Alarm, which has both a Genset and an AlarmDescription. The Genset is directly mappable via the GensetId property. The AlarmDescription should also be easily mappable off the Id property right? But it wasn't designed that way, and instead is mapped off a composite of (AlarmCode, PanelId) (note, they don't even share the same field name, found this out after struggling to find any relation until inspecting the data).
So, how would you map this using Fluent NHibernate? I've tried a couple variations, but have failed. Something like the following would be ... ideal, but I don't think anything like this is necessarily directly available.
References(x => x.AlarmDescription)
.Column("AlarmCode", m => m.EventTypeId)
.Column("PanelId", m => m.Genset.PanelId)
Have you tried formulas? (Sorry, I am not using fluent.)
<many-to-one name="AlarmDescription">
<column name="EventTypeId" />
<formula>(select g.PanelId from Generator g where g.Id = GensetId)</formula>
</many-to-one>
This requires declaring AlarmDescription primary key as being the composite id (AlarmCode, PanelId).
If you need to preserve AlarmDescription id, then add in it a natural-id as a component:
<class name="AlarmDescription">
<id name="Id">
<generator .../>
</id>
<natural-id>
<component name="AlarmDescriptionNaturalId">
<property name="AlarmCode" />
<property name="PanelId" />
</component>
</natural-id>
...
And reference it as the key for your relation in Alarm thanks to property-ref:
<many-to-one name="AlarmDescription" property-ref="AlarmDescriptionNaturalId">
<column name="EventTypeId" />
<formula>(select g.PanelId from Generator g where g.Id = GensetId)</formula>
</many-to-one>

Can NHibernate QueryOver have a SQL-where statement?

I'm new to Nhibernate. My problem is that I want to narrow down a query by using a column that is not included in my entity (ie hbm). I want to do something like this:
Session.QueryOver<MyEntity>()
.SQL_Where("MyFlag = 1")
Since I have no use of that flag later I don't want to include it to the entity
I know I can use:
Session
.CreateSQLQuery("SELECT A,B,C FROM ENTITY WHERE MyFlag = 1")
.SetResultTransformer(Transformers.AliasToBean<MyEntity>())
.List<MyEntity>();
It would be nice to use QueryOver<>(), it's more safe if a column is added etc.
You may be able to use filters:-
Put a filter on your mappings class definition, however this will affect ALL returned rows
e.g.
<class name="Domain.Model.MyEntity, Domain.Model" table="MyTable"
where="(MyFlag=1)">
...
</class>
or it may be possible to use conditional filters with QueryOver
<filter-def name="SetMyFlag">
<filter-param name=":flag" type="System.Int"/>
</filter-def>
<class name="Domain.Model.MyEntity, Domain.Model" table="MyTable">
...
<filter name="SetMyFlag" condition="(MyFlag=:flag)"/>
</class>
and use:-
session.EnableFilter("SetMyFlag").SetParameter("flag", 1);
session.QueryOver<MyEntity>();
Although I have never use conditional filters with unmapped columns so this may not work!

NHibernate query against the key field of a dictionary (map)

I have an object model where a Calendar object has an IDictionary<MembershipUser, Perms> called UserPermissions, where MembershipUser is an object, and Perms is a simple enumeration. This is in the mapping file for Calendar as
<map name="UserPermissions" table="CalendarUserPermissions" lazy="true" cascade="all">
<key column="CalendarID"/>
<index-many-to-many class="MembershipUser" column="UserGUID" />
<element column="Permissions" type="CalendarPermission" not-null="true" />
</map>
Now I want to execute a query to find all calendars for which a given user has some permission defined. The permission is irrelevant; I just want a list of the calendars where a given user is present as a key in the UserPermissions dictionary. I have the username property, not a MembershipUser object. How do I build that using QBC (or HQL)? Here's what I've tried:
ISession session = SessionManager.CurrentSession;
ICriteria calCrit = session.CreateCriteria<Calendar>();
ICriteria userCrit = calCrit.CreateCriteria("UserPermissions.indices");
userCrit.Add(Expression.Eq("Username", username));
return calCrit.List<Calendar>();
This constructed invalid SQL -- the WHERE clause contained WHERE membership1_.Username = #p0 as expected, but the FROM clause didn't include the MemberhipUsers table.
Also, I really had to struggle to learn about the .indices notation. I found it by digging through the NHibernate source code, and saw that there's also .elements and some other dotted notations. Where's a reference to the allowed syntax of an association path? I feel like what's above is very close, and just missing something simple.
Just trying to do this myself and it looks like this can be done with HQL but not the Criteria API.
https://nhibernate.jira.com/browse/NH-1795
To do it in HQL:
http://ayende.com/Blog/archive/2009/06/03/nhibernate-mapping-ndash-ltmapgt.aspx
Specifically look for Ayende's comment:
It is something like:
select 1 from Profile p join p.Entries e
where index(e) = 'HasCats' and e = 'true'

Nhibernate N+1 query problem

I need help with NHibernate. I'm using 2.1, but have tried this on 3 as well, with same results. Any help most appreciated!
When doing an ICriteria query using NHibernate, it executes both the query, and then for each result in the query, it executes another query to select an associated object, which is already returned in the initial resultset as I am using eager loading. This is of course resulting is dismal performance. Using the mapping file below, the query Nhibernate generates is exactly as expected below :
exec sp_executesql N'SELECT top 20 this_.ContactCode as ContactC1_48_1_, this_.IsActive as IsActive48_1_, contact2_.ContactCode as ContactC1_47_0_, contact2_.ContactFullName as ContactF2_47_0_ FROM Clients this_ left outer join Contacts contact2_ on this_.ContactCode=contact2_.ContactCode WHERE this_.ContactCode like #p0 and this_.IsActive = #p1 ORDER BY this_.ContactCode asc',N'#p0 nvarchar(7),#p1 bit',#p0=N'DAL001%',#p1=1
This query returns a single record, however is followed immediately with the following query, which is retrieving the details for the related contact object, which is already returned in full in the initial query, and of course, when returning many records, N addition queries are executed. This is completely unexpected!
exec sp_executesql N'SELECT contact0_.ContactCode as ContactC1_47_0_, contact0_.ContactFullName as ContactF2_47_0_ FROM Contacts contact0_ WHERE contact0_.ContactCode=#p0',N'#p0 nvarchar(6)',#p0=N'DAL001'
Lazy loading is switched off. The ICriteria code is as follows :
ICriteria clientsFromContactCodeQuery = session.CreateCriteria<Client>()
.Add(Restrictions.Like("ContactCode", id + "%"))
.Add(Restrictions.Eq("IsActive", true))
.AddOrder(Order.Asc("ContactCode"))
.SetMaxResults(maxResultCount);
var clientsFromContactCodeList = clientsFromContactCodeQuery.List();
I have a simple nhibernate mapping file :
<class name="Contact" table="Contacts" lazy="false">
<id name="ContactCode">
<generator class="assigned" />
</id>
<property name="ContactFullName" />
</class>
<class name="Client" table="Clients" lazy="false">
<id name="ContactCode">
<generator class="assigned" />
</id>
<property name="IsActive" />
<one-to-one
name="Contact"
class="Contact"
lazy="false"
fetch="join"
/>
</class>
Turn on lazy loading and then use HQL queries to prefetch required children.
from Client c
left join fetch c.Contact
where
c.ContactCode like :id
and
c.IsActive eq true
I don't know the order by syntax off the top of my head and the HQL may need to be tweaked a bit, but that's the core of the solution.
Try setting your max_fetch_depth property to 2 or 3 if you have not. Not sure if this works with one-to-one relationships though.
<property name="max_fetch_depth">3</property>
Most bizarrely, after trying various changes to the query, from hql to icriteria formats, nothing worked. However, I noticed some data where the N+1 queries were not being generated. It turns out the primary keys in the contacts and clients table has SPACES at the end, which when removed, fixed the problem!
Very strange, but thanks for the help.

Eagerly load recursive relation

I have a recursive one-to-many relationship that has the default lazy value of true. What code can I write against the NH API that will efficiently retrieve the ENTIRE tree AS IF I had lazy="false" on the SubCategories mapping?
Here's the recursive one-to-many relationship:
<class name="Category" lazy="false">
...
<list name="SubCategories" fetch="subselect">
<key column="ParentCategoryID"/>
<index column="PositionInList"/>
<one-to-many class="Category"/>
</list>
I don't specify lazy="false" on the list since laziness is required in about half the queries I need to run. I have fetch="subselect" on the list as an optimization for when I do manage to retrieve the entire tree.
I've tried the ICriteria API:
session.CreateCriteria<Category>().SetFetchMode( "SubCategories", FetchMode.Eager ).Add( Restrictions.IsNull("ParentCategory") ).SetResultTransformer( CriteriaSpecification.DistinctRootEntity ).List<Category>();
but that only eagerly loaded only the first level in the hierarchy.
See Ayende's site: Efficiently Selecting a Tree. I have successfully used this technique in my own applications. With ICriteria, it looks like this:
session.CreateCriteria<Category>()
.SetFetchMode("SubCategories", FetchMode.Join)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Category>()
.Where(x => x.ParentCategory == null);
The main difference between this version and what you tried is how the "ParentCategory == null" filter is applied. It has to be left out of the query that is sent to the database in order to retrieve the whole tree - but we still need the query to only return the root nodes of the tree, so we'll use linq to find those after the database query has completed.
I used Daniel's code as a bases for solving the problem. I also experimented with the equivalent HQL that I shared below. The HQL executed slightly faster, but I went with ICriteria since I could then choose between FetchModel.Join and FetchModel.Lazy.
session.CreateQuery( "from Category as c left join fetch c.SubCategories" )
.SetResultTransformer( new DistinctRootEntityResultTransformer() )
.List<Category>()
.Where( c => c.ParentCategory == null );
Not sure if it helps but take a look at : map a tree in NHibernate