Recently I’ve started to play with Linq to NHibernate and I have to say it work pretty well for the scenarios I’m facing. It also helped me to ‘re-learn’ some things on NHibernate I usually forget.
Let’s consider the case in which we have a class that have some properties without setters, the usual way to map them is using the access=’field’ modifier and tell NHibernate to not use the property by the internal field of the implementation; you can then write your HQL queries on that field, here’s an example:
Public Class IDGEN
Private mID As Int64 = -1
Public Overridable ReadOnly Property ID() As Int64
Get
Return mID
End Get
End Property
Private mYear As Int16
Public Overridable ReadOnly Property Year() As Int16
Get
Return mYear
End Get
End Property
End Class
Here is the mapping I was used to have:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Entities.IDGEN, CommunicationModule" table="tblPROTOCOL_ID_GENERATOR" lazy="false" >
<id name="mID" column="ID" type="Int64" access="field" unsaved-value="-1">
<generator class="native" />
</id>
<property name="mYear" column="Year" type="Int16" access="field" insert="false" update="false" />
</class>
</hibernate-mapping>
You see we refer to the internal implementation members and our HQL queries will look like:
select max(id.mID) from IDGEN id where id.mYear = :year
As an experiment I’m porting this code to use Linq to NHibernate, the link query I would like to write is this:
long idgen = Session.Linq<Idgen>().Where(i => i.Year == year).Max(i => i.ID);
You can easily see the problem here: in code I cannot refer to the private mYear or mID fields, so when the query translator tries to parse my expression I get a wonderful exception.
How to make this work ? The answer is simple and built into NHibernate we can use Access and Naming strategies, look at 'Chapter 5.1.9 property' of the NHibernate reference documentation.
There you can see how to tell NHibernate to use the Getter of the properties to retrieve values from the class, and how the internal field is called so it can use that when assigning values to object.
In the end it’s just a matter of writing the right mappings, with something like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="SID.Gestione.RefertiDocumenti.Neurologia.Entities.Idgen, SID.Gestione.RefertiDocumenti.Neurologia" table="IncrementalNumberGenerator" lazy="false" >
<id name="ID" column="ID" type="Int64" access="nosetter.pascalcase-m" unsaved-value="-1">
<generator class="native" />
</id>
<property name="Year" column="Year" access="nosetter.pascalcase-m" type="Int16" insert="false" update="false" />
</class>
</hibernate-mapping>
we are now able to make the former Linq query work.
We now refer to the actual property of the class, we specify we don’t have setter and we use the pascalcase-m notation for the internal fields.
So fixing all your previous mapping that used access=’field’ attribute to a more correct notation you can easily perform Linq queries on all your classes.
Related Content
- NHibernate - Customize the Linq provider to call your user defined SQL functions (10/01/2010)
- NHibernate Linq provider: dynamic filtering using lambda expressions (26/08/2015)
- NHibernate 3 - Extending the Linq Provider to fix some System.NotSupportedException (26/08/2015)
- Linq to NHibernate - String.Equals with StringComparison option (26/08/2015)
- VSeWSS: how to solve the ‘SecurityException: Access denied’ issue (26/08/2015)
- More related document (22)