views:

243

answers:

1

Hello,

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>
+1  A: 

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?

Jaguar
Thanks for your help, it's given me a leg up. Unfortunately, it doesn't work :( NH complains about "duplicate association path: LocalizedProperty". I'm currently investigating mapping the 2nd and 3rd LocalizedProperty aliases with views ...
Paul Lalonde
have you tried usingcrit.CreateAlias("LocalizedProperty", "lp1");it will probably throw the same error but you have nothing to loose
Jaguar
meh it seems that it is not supported... check http://nhjira.koah.net/browse/NH-2016... if you really want to port the code from the DB to your app, an ISQLQuery is the only way
Jaguar