Linq to LLBLen and grouping clauses

Posts   
 
    
JeffN825
User
Posts: 33
Joined: 04-Jun-2008
# Posted on: 29-Sep-2008 08:36:41   

I would like/need to flatten my object heirarchy for databinding. I'd like to see about binding to an IQueryable supported grid like DevExpress's. Really I only need the IQueryable implementation for my CustomerName and OrderDate fields (that is, I only need on demand querying based on these fields). I've tried every implementation I can think of to make this work.

From reading elsewhere, I understand that we cannot have grouping clauses such that a group by date can only consist of that DateTime field alone. (ie. group item by new {item.Name, item.Date } into grouping is a no go).

Additionally, the correlation requirements have made this a bit more difficult. I cannot figure out a way of doing the following effectively: var result= from o in metaData.Order select new { Date=o.Date Addresses=o.Customer.Addresses (throws an error....)

The closest I've gotten to a working query (I guess) is:

var datasource = ( from order in metaData.Order group order by order.Date into outerGrouping from g in outerGrouping where g != null group g by g.CustomerID into innerGrouping where innerGrouping != null select new { Name=new CustomerEntity(innerGrouping.Key.CustomerID).Name } the above throws a null reference exception...

Ideally, what I really want is this: var datasouce= from order in metaData.Order group order by new {order.Date, order.Customer} into grouping // won't work (no grouping on entites, no multiple group expressions with DateTimes) select new { CustomerName=grouping.Key.Customer.Name // won't work because of above and correlation constrains Addresses=grouping.Select(order=>order.Customer.Addresses) //won't work because of correlation Date=order.Date }

Note that an acceptable solution would be to use in memory projections for everything except the Date and Name fields (if it makes any difference): var datasource= from order in metaData.Order select new { Addresses=order.Customer.Addresses.ToList() // doesn't work either, even though I just want to force the expansion to IEnumerable }

Is anything like this possible?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 29-Sep-2008 09:37:02   

JeffN825 wrote:

var result=
from o in metaData.Order
select new {
Date=o.Date
Addresses=o.Customer.Addresses 

(throws an error....)

What is _o.Customer.Addresses _ (string, Entity, EntityCollection) ? and... when an exception is thrown, post the exception message and stack trace.

For the rest, please post the approximate SQL query you would like to reproduce, as the descriptions are a little blur.

Also, make sure you have the latest LLBLGenPro v2.6 build.

David Elizondo | LLBLGen Support Team
JeffN825
User
Posts: 33
Joined: 04-Jun-2008
# Posted on: 29-Sep-2008 16:33:31   

Addresses is an EntityCollection (of AddressEntity). I'll get back to you shortly with the exact exception but it's basically that no correlation filter is found (presumably because it's not immediately off the root entity - the OrderEntity).

I'm using 2.6 Final June 6, 2008. Is that not the latest? It looks like the demo version for download on the site says updated 9/16/2008.

As for the SQL, I'll also get back to you when I have a free minute later today.

Thanks for your quick response.

P.S. I'm using LLBLGen with an Access 2003 format database, if that makes any difference.

JeffN825
User
Posts: 33
Joined: 04-Jun-2008
# Posted on: 30-Sep-2008 04:16:37   

The latest code fixed the problem with grouping.

I can now: group order by new {order.Date, order.CustomerID} successfully.

I am still having the problem with projection within in the grouping clause: var orderInfo = // an anonymous type object (from order in metaData.Order group order by new {order.Date, order.CustomerID } into grouping select new { CustomerName=new CustomerEntity(grouping.Key.CustomerID), // works ok CustomerAddresses=grouping.Select(item=>item.Customer.Addresses) // an entity collection off customer, fails

}).ToList()

I have tried several varieties of this query, using where clauses to try to establish the correlation with the grouping construct, all without success.

The exact exception is: Couldn't create any correlation filter lambda because the nested query didn't have any correlation filters. Please specify a filter in the nested query to tie the nested query to the parent query

The stacktrace: " at SD.LLBLGen.Pro.LinqSupportClasses.ValueListProjectionDefinition.PostProcessNestedQueries( ITemplateGroupSpecificCreator frameworkElementCreator, IElementCreatorCore generatedCodeElementCreator, MappingTracker trackedMappings)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder .HandleProjectionExpression(ProjectionExpression expressionToHandle)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler .HandleExpression(Expression expressionToHandle)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder. HandleExpression(Expression expressionToHandle)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler. HandleSelectExpression(SelectExpression expressionToHandle, SelectExpression newInstance)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler. HandleSelectExpression(SelectExpression expressionToHandle)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder. HandleSelectExpression(SelectExpression expressionToHandle)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler. HandleExpression(Expression expressionToHandle)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder. HandleExpression(Expression expressionToHandle)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.HandleExpressionTree( Expression expression)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq. IQueryProvider.Execute(Expression expression)\r\n at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.Execute()\r\n at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.System.Collections. Generic.IEnumerable<T>.GetEnumerator()\r\n at System.Collections.Generic.List1..ctor(IEnumerable1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)"

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 30-Sep-2008 12:18:19   

Please post the SQL query (queries) you want to reproduce.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39900
Joined: 17-Aug-2003
# Posted on: 30-Sep-2008 17:02:24   

Additionally: this is the cause: CustomerAddresses=grouping.Select(item=>item.Customer.Addresses)

is effectively a nested query, though 'grouping' doesn't exist till the query is executed, so it can't make additional constructs out of it. This is caused by the fact that it's an IGrouping<> typed element, and not just a flat set of data, but a nested structure: for every key there are n rows of data below it in a hierarchy. It might be linq to sql executes this properly, but that's due to the fact it postpones this query for every row till the main query is enumerated (so for every row a query is executed).

So with the description what you want to achieve we might be able to guide you to a construct which is supported.

Frans Bouma | Lead developer LLBLGen Pro
JeffN825
User
Posts: 33
Joined: 04-Jun-2008
# Posted on: 01-Oct-2008 04:49:22   

select * from procedurefile as pf, (select p.procedureid, p.patientid, p.datetime from [procedure] as p group by p.patientid, p.datetime, p.procedureid) as g where g.procedureid=pf.procedureid

in the terms of the actual data model I'm using...

and the linq query is

from procedure in context.Procedure group procedure by new {procedure.PatientID, procedure.DateTime} into grouping select new { PatientID=grouping.Key.PatientID, Date=grouping.Key.DateTime, Files=grouping.Select(g=>g.ProcedureFiles) }

Because of the way I need to bind to data, and becuase of the fact that I'd like my data source to by IQueryable (not yet executed) (for the DevExpress Linq data source), I need a 1 shot query that projects my data as desired.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 01-Oct-2008 11:55:25   

select * from procedurefile as pf, (select p.procedureid, p.patientid, p.datetime from [procedure] as p group by p.patientid, p.datetime, p.procedureid) as g where g.procedureid=pf.procedureid

Would the group by have any effect in the above query taking into consideration that it contains the PK?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39900
Joined: 17-Aug-2003
# Posted on: 01-Oct-2008 12:12:57   

Do: var q = from procedure in context.Procedure group procedure by new {procedure.PatientID, procedure.DateTime} into grouping select grouping;

q is now an IEnumerable<IGrouping<...>> which you can enumerate per IGrouping, which has as key an anonymous type with 2 fields, PatientID and DateTime, and you can enumerate the IGrouping for the procedure entities.

Frans Bouma | Lead developer LLBLGen Pro
JeffN825
User
Posts: 33
Joined: 04-Jun-2008
# Posted on: 02-Oct-2008 05:13:29   

That query works....but the catch is I'd like to bind the IQueryable itself to a data source (the DevExpress one) - meaning I need one query to do it all. On user interaction, I need to interact with the data, so binding to an anonymous type (select grouping from your post) won't work. For example on cell click, I'd want to get the PatientID from the grouping, but that wouldn't work because the select is projecting an anonymous type:

int patientID = (int)cell.CellDataSource; // wouldn't work

where the cell is in the column bound to the property PatientID

I would need to

select new { PatientID=grouping.Key.PatientID Date=grouping.Key.Date }

for this to work.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39900
Joined: 17-Aug-2003
# Posted on: 02-Oct-2008 10:00:23   

I'm sorry, but the query I gave you does exactly the same as what you asked for, i.e. it gives the data you need. I don't really understand what the problem is you're having, as you can bind the query I gave you to whatever you like, it has IGrouping<key, Procedure>, where 'key' is an anonymous type with two fields, PatientID and DateTime. So you can enumerate over the query, and for each entry in the query, you have the PatientID and DateTime and then enumerate over that entry to get all the procedures. EXACTLY the same as your example.

Now, it could be that the devexpress grid and databinding gets in your way to bind this easily. If so, do a .ToList() of this query, which will give you the grouped results in teh format above, then do a simple linq to objects query over it to produce the types you want:

var groupedResults = q.ToList(); var bindableResults = from g in groupedResults select new { g.Key.PatientID, g.Key.DateTime, Procedures = g};

then bind bindableResults to the grid.

Group by queries in linq are very tricky as the group by in linq is different from a group by in a database. this means that to return a group by query in linq using sql will take sometimes 2 queries as a group by in a db is a flat list, not a hierarchy like the linq equivalent. Therefore in this case this route is the way to go. Normally you wouldn't need the linq to objects query at all, but for databinding you might need it.

Frans Bouma | Lead developer LLBLGen Pro