Group selection

Posts   
 
    
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 07-May-2008 11:00:23   

Hi Frans,

This query fails:

using (DataAccessAdapter adapter = new DataAccessAdapter("Data Source=XXX;Initial Catalog=Northwind;Integrated Security=True"))
                {
                    var query = from c in new LinqMetaData(adapter).Customer
                                group c by c.CompanyName into g
                                select g;
                    gridControl1.DataSource = query.ToList();
                }

It yields ArgumentException:

No selectList specified in selectList.
Parameter name: selectList

Even changing select to something like:

select new { Key=g.Key, Values=g }

fails.

This dude yields IndexOutOfRangeException:

   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleNewExpression(NewExpression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleExpression(Expression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleLambdaExpression(LambdaExpression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleExpression(Expression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleMethodCallSelect(MethodCallExpression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleQueryableExtensionMethod(MethodCallExpression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleMethodCallPerType(MethodCallExpression expressionToHandle, Type declaringType)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleMethodCallExpression(MethodCallExpression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.PreProcessor.HandleExpression(Expression expressionToHandle)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.HandleExpressionTree(Expression expression)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery`1.Execute()
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Client.Form2.OnLoad(EventArgs e) in C:\Progs\NwTest\NwTest\Client\Form2.cs:line 32
   at System.Windows.Forms.Form.OnCreateControl()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.WmShowWindow(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ContainerControl.WndProc(Message& m)
   at System.Windows.Forms.Form.WmShowWindow(Message& m)
   at System.Windows.Forms.Form.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.SafeNativeMethods.ShowWindow(HandleRef hWnd, Int32 nCmdShow)
   at System.Windows.Forms.Control.SetVisibleCore(Boolean value)
   at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
   at System.Windows.Forms.Control.set_Visible(Boolean value)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at Client.Program.Main() in C:\Progs\NwTest\NwTest\Client\Program.cs:line 18
   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 07-May-2008 12:13:28   

You're using the latest beta?

Both reproduced. Looking into it.

(edit) This is likely caused by the fact that the queries don't make any sense. The thing is that the type of 'g' is a list of grouping instances, but these fellows don't have a ctor which is usable.

If you do: foreach (var v in q) { // check here }

and you check at checkhere the contents of v, you'll see it is a grouping instance, with 1 property. However it's an interface implementation. The object implementing the interface is unknown. I can't instantiate the IGrouping instance. Looking into the Linq to Sql code, I see that they implement the interface manually and use that to materialize the resultset.

However, when I run the query with linq to sql, I get 1 query for the keys and for every key value a new query... So definitely not the select .. from customers groupby CompanyName

This is logical, as SQL can't deal with this: you have to group on EVERY field in the projection.

So in short: what kind of query do you want to run ?

Frans Bouma | Lead developer LLBLGen Pro
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 07-May-2008 12:21:01   

I think so, they are dated 30.4. and the LinqSupportClasses file version is 2.6.8.416.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 07-May-2008 12:29:14   

See my update of my previous post simple_smile

If I look at the linq to sql output, it looks like they're doing a groupjoin instead of a groupby, i.e.: per key a set of entities is fetched. THis is in fact a nested query hence the truckload of additional queries on their part.

I could 'hack' this in with a special case code path, where the groupby reference is converted into a nested query, as it seems (looking again at the docs) that is what group x by y means: group x under the key y in sets of x's.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 07-May-2008 13:10:21   

Hmm... it's a different way of using the grouping feature. Normally, when you think in terms of databases, you'd group to run an aggregate function on the grouped sets. But in this case, you simply want to create a dictionary kind of set: per key the matching elements have to be grouped under that set in the end result.

Could be very useful. I'll give it a try to add this.

Frans Bouma | Lead developer LLBLGen Pro
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 07-May-2008 14:02:26   

Sorry for being late. Yes, it is useful to get grouping rows + actual rows that are part of each group. You can't do that with plain sql easily like that. I always shows this feature when doing LINQ to SQL presentations.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 07-May-2008 15:13:48   

mihies wrote:

Sorry for being late. Yes, it is useful to get grouping rows + actual rows that are part of each group. You can't do that with plain sql easily like that. I always shows this feature when doing LINQ to SQL presentations.

(then don't show the # of queries executed wink stuck_out_tongue_winking_eye )

I'll give it a try later today. It's tricky I think, but the code is there so it should be possible to add this. The only drawback is to figure out when to build this special projection...

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 07-May-2008 19:03:51   

I think it's doable. It's a bit of work though... however the 'g' ends up as a SetReferenceExpression in the projection, and it is detectable if that's a groupby set. What then should be done is transform that setreferenceexpression into a nested query.

thus: ... select g;

becomes ... select new Grouping<keyType, IEnumerable<setType>> { g.Key, Values = gAsQueryWithoutGroupByClause};

Which then flows into the nested query engine just fine. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 08-May-2008 16:44:26   

Almost there, the fake nested query is constructed, it needs a different expression to link it to the proper parent. Hopefully I get it finished today.

There's a workaround though. simple_smile

See this example:


var q = from c in metaData.Customer
        where c.Country!=null
        group c by c.Country into g
        select new
        {
            g.Key,
            CustomersInCountry = (
            from c2 in metaData.Customer
            where g.Key == c2.Country
            select c2)
        };

Not pretty, but it works. 2 queries. Where the key is null it goes wrong in the projector as the resultset is of the wrong type, looking into that little issue (not related to this thread).

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 09-May-2008 13:11:25   

Got it working sunglasses

var q = from c in metaData.Customer group c by c.Country into g select new { g.Key, CustomersInCountry=g };

and

var q = from c in metaData.Customer where c.Country!=null group c by c.Country into g select g;

now work as expected. the first loads in CustomersPerCountry the ... customers per key (country) and the second produces a list of IGrouping<string, customer> instances, with 1 instance per key.

using 2 queries, 1 for the keys (where the group by is in) and 1 for the data to group. (take that, linq to sql wink )

Added to next build (which will be out in the next few days)

Frans Bouma | Lead developer LLBLGen Pro
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 09-May-2008 14:32:20   

Hey Frans,

Nice work. No need to hurry with this update. simple_smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 12-May-2008 17:22:04   

Build is now available simple_smile

Frans Bouma | Lead developer LLBLGen Pro