Adding a predicate to a subquery in FetchProjection override

Posts   
 
    
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 25-Jul-2017 13:50:57   

Hello!

I have overridden the FetchProjection method to add a predicate to a query.


public override void FetchProjection(List<IDataValueProjector> valueProjectors, IGeneralDataProjector projector, QueryParameters parameters)
{
     var field = .....

     parameters.FilterToUseAsPredicateExpression.Add(field.Equal(1));
    
     base.FetchProjection(valueProjectors, projector, parameters);      
}

It work fine except for scalar queries. I construct my scalar queries in the following way


var qf = new QueryFactory();

var dynamicQuery = .....

var scalarQuery = qf.Create().Select(dynamicQuery .CountRow());

The problem with this code is, the predicate is added the query which wrapps the dynamicQuery. I'd need to access the the dynamicQuery from the FetchProjection override and add the predicate to it. Is this possible ?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 26-Jul-2017 06:55:22   

The problem as I see, is that you don't know in advance how the query would look like in certain instance. It could be very simple or very complex, and you might want the predicate inserted somewhere deep in the query graph.

  • How your dynamicQuery looks like?
  • What LLBLGen version are runtime library version are you using? (http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7722)
  • Did you consider to use a method outside to do the job adapting your query, instead of hook the query in a general manner in a FetchProjection overload?
David Elizondo | LLBLGen Support Team
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 31-Jul-2017 09:51:15   

The problem as I see, is that you don't know in advance how the query would look like in certain instance. It could be very simple or very complex, and you might want the predicate inserted somewhere deep in the query graph.

Actually I know. It will be one level deep.


var scalarQuery = qf.Create().Select(dynamicQuery .CountRow());

I just need to add the predicate always to the dynamicQuery.

What LLBLGen version are runtime library version are you using?

4.2.14.1212

Did you consider to use a method outside to do the job adapting your query, instead of hook the query in a general manner in a FetchProjection overload?

The thing is we have already a working system with many queries. I need to filter all results by company id. I thought it would be better to have this functionality in one place then scattered over the whole system. This override works fine for all non scalar queries, so it would be nice to get it work also withthe scalar queries. simple_smile

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 31-Jul-2017 23:46:03   

ScalarQueries like CountRow and such always wraps your query and using the fetchProjection will add to the outer query.

I can't see a way to access the inner query.

But if the field you are filtering on (CompanyID) is in the select list, it can still work with the predicate on the outer scalar query.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Aug-2017 09:44:15   

Well, there is a way simple_smile The inner query is a DerivedTable definition in a DynamicRelation instance in the relationcollection of the query. If you use a debugger you can see that setup. The DerivedTable definition can be altered, you can add your predicate to that object.

Frans Bouma | Lead developer LLBLGen Pro
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 01-Aug-2017 13:27:18   

Well, there is a way Regular Smiley The inner query is a DerivedTable definition in a DynamicRelation instance in the relationcollection of the query. If you use a debugger you can see that setup. The DerivedTable definition can be altered, you can add your predicate to that object.

Do you mena QueryParameters.RelationsToUse ? It is an collection of IRelationCollection type. But this collection is empty in my case.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Aug-2017 15:44:17   

Hmm. Will check.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Aug-2017 16:37:13   

Ah, of course simple_smile -> the CountRow() creates a ScalarQuery, wrapping the one it's called on. This is a scalarqueryexpression and it's added to an EntityField object's ExpressionToApply, which is the field in the projection.

So, QueryParameters.FieldsForQuery[0].ExpressionToApply is your ScalarQueryExpression. This expression contains the RelationCollection which contains the Dynamic relation. Which left operand is the derived table you want to alter.

So in short:

var dt = ((DynamicRelationBase)((ScalarQueryExpression)parameters.FieldsForQuery[0].ExpressionToApply).RelationsToUse[0]).LeftOperand;

This is rather convoluted if you look at it this way, but converting this to SQL is surprisingly straightforward. The API has evolved over the years so it could use the existing API without breaking anything (it's still using the same low level core API as v1)

Now, there's a class in the runtime, 'DerivedTableFinder'. You simply pass the entity field to its Traverse() method and it will traverse the complete object graph. This might find more Derived tables than you need (also deeper nested ones are found), but could give a way to find them easily.

Hope this helps.

Frans Bouma | Lead developer LLBLGen Pro
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 02-Aug-2017 11:27:54   

I got it working simple_smile


  DerivedTableDefinition dt = (DerivedTableDefinition)((DynamicRelationBase)((ScalarQueryExpression)parameters.FieldsForQuery[0].ExpressionToApply).RelationsToUse[0]).LeftOperand;

   dt.Filter.Add(field.Equal(1)));


I didn't even needed the DerivedTableFinder class.

Thank you!