nhibernatecastle-activerecord

Querying both a base class and a derived class in a single queryover


So this seems a little complicated on my side, so I'll try and first set the scene as clearly as possible.

[ActiveRecord, JoinedBase]
abstract BaseClass
{
    public x;
    public y;
}

[ActiveRecord, JoinedBase]
ClassA : BaseClass { }

[ActiveRecord, JoinedBase]
ClassB : BaseClass { }

abstract GraphicBaseClass : BaseClass
{
    public height;
    public width;
}

[ActiveRecord, JoinedBase]
ClassC : GraphicBaseClass { }

[ActiveRecord, JoinedBase]
ClassD : GraphicBaseClass { }

[ActiveRecord, JoinedBase]
ClassE : GraphicBaseClass { }

Note: several keywords and other fluff ignored for saving on the wall of text I need to enter.

So the textual explanation of that... we have a standard base class "BaseClass" that implements a number of properties. We then have a couple of classes "ClassA" and "ClassB" that are specific types of the base. We then use an intermediate base class "GraphicBaseClass" that adds height and width properties, which the remaining 3 classes inherit from. This results in the single BaseClass common table, and the 5 specific tables for each of the concrete classes.

I have a query that in the majority works on the base properties. In certain circumstances I want to query on the height and width. As GraphicBaseClass is not an activerecord class I cannot use that as the base of the query. Currently I'm using the QueryOver syntax, but I can't find a way to query the three GraphicBaseClass derived entities with the height and width properties.

The closest I got so far was:

QueryOver<BaseClass, BaseClass> query = QueryOver.Of<BaseClass>()
    .Where(bc => bc.x == something)
    .TransformUsing(CriteriaSpecification.DistinctRootEntity);
if (actualTypes.All(x => x.IsSubclassOf(typeof (GraphicBaseClass))))
{
    // Filter by dimensions
    if (criteria.RestrictSize)
    {
        if (criteria.FilterByDimension)
        {
            query.DetachedCriteria.Add(Restrictions.Lt("Width", criteria.Width + 10));
            query.DetachedCriteria.Add(Restrictions.Lt("Height", criteria.Height + 10));
        }
    }
}

This resulted in sql something like:

SELECT TOP (4 /* @p0 */) this_.BaseClassId as BaseClass1_97_5_,
                 this_.Version                   as Version97_5_,
                 this_.BaseClassType          as BaseClass2_97_5_,
                 this_1_.Width                   as Width99_5_,
                 this_1_.Height                  as Height99_5_,
                 this_2_.Width                   as Width100_5_,
                 this_2_.Height                  as Height100_5_,
                 this_3_.X                       as X101_5_,
                 this_4_.Width                   as Width102_5_,
                 this_4_.Height                  as Height102_5_,
                 this_5_.X                       as X103_5_,
FROM   BaseClass this_
       left outer join ClassC this_1_
         on this_.BaseClassId = this_1_.ClassCId
       left outer join ClassD this_2_
         on this_.BaseClassId = this_2_.ClassDId
       left outer join ClassA this_3_
         on this_.BaseClassId = this_3_.ClassAId
       left outer join ClassE this_4_
         on this_.BaseClassId = this_4_.ClassEId
       left outer join ClassB this_5_
         on this_.BaseClassId = this_5_.ClassBId
WHERE  and this_1_.Width between 0 /* @p8 */ and 'Height' /* @p9 */

So it's only applying the height and width restriction to one of the 3 required classes.

I am looking for any answer here, if it can be done in HQL or the Criteria API or whatever is required to achieve the result.

Apologies for the wall of text!!

Thanks.


Solution

  • A bit late, I know. Here is the solution I usually use:

    Create a subquery and join it with "in".

    if (/* subclass required*/)
    {
      var subClassQuery = QueryOver.Of<GraphicBaseClass>();
      subClassQuery.Where(x => x.Width  == criteria.Width);
      subClassQuery.Where(x => x.Height == criteria.Height);
      subClassQuery.Select(x => x.Id);
      query.Where(Subquery.WherePropery(x => x.Id).In(subClassQuery));
    }