NHibernate / QueryOver: How to left join with parameter

5.5k views Asked by At

Is there a way in NHibernate / QueryOver API to pass a parameter already in the mapping (so it uses the parameter as a fixed value for all queries on this particular instance).

I need this (or a workaround) because I have a view like this in the Database:

CREATE VIEW ProblematicView
AS
SELECT
    v.*,
-- lots of data
FROM someView v
LEFT JOIN someTable t ON v.ForeignKey = t.ForeignKey

Now additionally to the ForeignKey match I need an additional check for a property like this:

AND t.SomeOtherValue = @myParameter

which is not possible as there is no way to pass parameters to a view directly. With a table valued function this would be possible but then I don´t know how to map it to NHibernate / QueryOver.

Also the function approach would be hard to realize as a huge QueryOver statement is used to filter all the remaining properties (as the view is used for searching business entities)

Currently I am applying the SomeOtherValue / @myParameter filter to the the entire view as a part of my QueryOver.

This is my main problem:

Using for example:

SELECT
    v.*,
-- lots of data
FROM someView v
LEFT JOIN someTable t ON v.ForeignKey = t.ForeignKey AND t.SomeOtherValue = 123
 (followed by alot of other property checks...)

will return a different result (NULL entries for t.SomeOtherValue included on intent due to left join)

than using:

SELECT * FROM ProblematicView where SomeOtherValue = 123
 (followed by alot of other property checks)

As now the left join happens inside the view without checking for SomeOtherValue and as the SomeOthervalue check is applied independent on the left join, all the NULL values will be excluded (which is wrong business logic).

Also using:

SELECT * FROM ProblematicView where SomeOtherValue = 123 OR SomeOtherValue = NULL
 (followed by alot of other property checks)

does not seem to help, as NULL values are still ignored...

So the only way I can imagine to solve this problem would be to find a way to pass my SomeOtherValue property somehow to the view so that it can use it as a parameter in the view itself (instead of in the where clause) or maybe somehow use an sql table based function with parameter for the model...

EDIT:

After some more research I managed to hopefully simplify the problem:

I am trying to convert this SQL:

Select v.*, ... from (someView v LEFT JOIN someTable t ON v.ForeignKey = t.ForeignKey) 
WHERE SomeOtherValue = 123

(where SomeOtherValue comes from someOtherTable)

to this:

Select v.*, ... from someView v LEFT JOIN someTable t on v.ForeignKey = t.ForeignKey
AND t.SomeOtherValue = 123

using NHibernate / QueryOver. Note that in the second version the property SomeOthervalue is checked against directly within the left join, where in the first version it is incorrectly applied only after the left join.

I need to find a way to write latter SQL statement in a way that I can put it inside a view while still being able to pass 123 as parameter for SomeOtherValue.

1

There are 1 answers

3
Andrew Whitaker On

You can add a condition to a join clause using the overload of JoinQueryOver or JoinAlias that takes a withClause parameter. For example:

SomeTable stAlias = null;

session.QueryOver<ProblematicView>()
    .Left.JoinAlias(
        pv => pv.SomeTable,              // Join path
        () => stAlias,                   // alias assignment
        st => st.SomeOtherValue == 123)  // "with" clause
    /* etc. */

The "with" portion of the QueryOver join is what will add a condition to the left outer join in SQL. The above should generate this:

SELECT /* select list */
FROM   [ProblematicView] this_
       left outer join [SomeTable] sometable1_
         on this_.Id = sometable1_.ProblematicViewId
            and (sometable1.SomeOtherValue = 123 /* @p0 */)

If I understand correctly, this should help solve your problem.

A few things to note about adding a "with" clause:

  • Interestingly, all of the overloads of JoinQueryOver and JoinAlias that allow you to specify a "with" clause require that you assign an alias when you do the join.
  • You cannot (as far as I know) generate SQL with an or in the join condition. the "with" clause is always anded with the mapped join condition (i.e., FK → PK)