Question

I have an NHibernate application that currently makes use of a SQL Server user-defined function. I would like to avoid having to call this function, and instead express its logic using the NH criteria API. Unfortunately, I'm having difficulty applying the criteria examples in the NH documentation to my particular case. So I'm turning to this site for help.

Here is the function. It takes a single string argument and returns a table. The function performs 3 joins between the same 2 tables but with different join criteria, and then coalesces the result.

Any hints would be appreciated. Thanks in advance!

Edit: This is targeting NH 2.1

Edit: A comment in the accepted answer states that the conversion isn't possible, which is the correct answer.

CREATE FUNCTION dbo.GetRevisionText
(
  @LangId NVARCHAR(16)
)
RETURNS TABLE
AS
RETURN
(
  SELECT r.RevisionId,
    COALESCE(lp1.Title, lp2.Title, lp3.Title) Title,
    COALESCE(lp1.Description, lp2.Description, lp3.Description) Description
    FROM Revision r
      LEFT JOIN LocalizedProperty lp1
        ON lp1.RevisionId = r.RevisionId
        AND lp1.LanguageId = @LangId
      LEFT JOIN LocalizedProperty lp2
        ON lp2.RevisionId = r.RevisionId
        AND lp2.LanguageId = LEFT(@LangId, 2)
      LEFT JOIN LocalizedProperty lp3
        ON lp3.RevisionId = r.RevisionId
        AND lp3.LanguageId = r.DefaultPropertiesLanguage
);

Here is the mapping for the 3 classes involved :

<class name="Revision">
  <id name="RevisionId" type="Guid">
    <generator class="assigned"/>
  </id>
  <set name="LocalizedProperties" inverse="true" lazy="true" cascade="all-delete-orphan">
    <key column="RevisionId"/>
    <one-to-many class="LocalizedProperty"/>
  </set>
  <many-to-one name="DefaultPropertiesLanguage" class="Language" not-null="true"/>
</class>

<class name="Language">
  <id name="LanguageId" type="String" length="16">
    <generator class="assigned"/>
  </id>
  <property name="Lcid" type="Int32" unique="true" not-null="true"/>
</class>

<class name="LocalizedProperty" mutable="false">
  <composite-id>
    <key-many-to-one name="Revision" class="Revision" column="RevisionId"/>
    <key-many-to-one name="Language" class="Language" column="LanguageId"/>
  </composite-id>
  <property name="Title" type="String" length="200" not-null="true"/>
  <property name="Description" type="String" length="1500" not-null="false"/>
</class>
Was it helpful?

Solution

this might do the job (NH 1.2):

var crit = nhSes.CreateCriteria(typeof(Revision), "r")
 .SetProjection(
  Projections.SqlProjection(@"r.RevisionId as rid,
    COALESCE(lp1.Title, lp2.Title, lp3.Title) as Title,
    COALESCE(lp1.Description, lp2.Description, lp3.Description) as Description", new[] {"rid", "Title", "Description"}, new[] {NHibernateUtil.Guid, NHibernateUtil.String,NHibernateUtil.String})
 );

crit.CreateCriteria("LocalizedProperty", "lp1", JoinType.InnerJoin);
crit.CreateCriteria("LocalizedProperty", "lp2", JoinType.InnerJoin);
crit.CreateCriteria("LocalizedProperty", "lp3", JoinType.InnerJoin);

crit.Add(Expression.Eq("lp1.LanguageId", langId));
crit.Add(Expression.Sql("lp2.LanguageId = LEFT(:LangId, 2)", langId, NHibernateUtil.String));
crit.Add(Expression.EqProperty("lp3.LanguageId", "r.DefaultPropertiesLanguage"));

note however that this does not generate ANSI syntax join but places the constraints in a WHERE clause. I don't really think that's a problem, you are doing an inner join and not an outer join.

Also i don't remember now the proper parameter notation on Expression.Sql. I've defined it as :LangId although it may also be @LangId

sidenote: as you can see although this is a criteria query its just a bunch of sql-statements split so to fit the criteria API; are you sure that this is what you need?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top