Groupjoin throws Index was outside the bound of the array

Posts   
 
    
AlexanderM
User
Posts: 41
Joined: 18-May-2012
# Posted on: 05-Sep-2014 15:36:03   

Hello,

I have this Linq Query in LinqPad and it throws an exception "Index was outside the bound of the array".

var poolEmps = (from pe in PoolEmployee 
where pe.PoolId == 1783
join poe in PropertyOfEmployee on pe.Id equals poe.PoolEmployeeId
into groupedProperties
select new {PoolEmployee = pe, Properties = groupedProperties}).ToList();

I'm using version 4.2.14.811.

stacktrace: at lambda_method(Closure , Object[] , Int32[] ) at SD.LLBLGen.Pro.LinqSupportClasses.DataProjectorToObjectList1.AddRowToResults(IList projectors, Object[] rawProjectionResult) at SD.LLBLGen.Pro.ORMSupportClasses.ProjectionUtils.FetchProjectionFromReader(List1 valueProjectors, IGeneralDataProjector projector, IDataReader datasource, Int32 rowsToSkip, Int32 rowsToTake, Boolean clientSideLimitation, Boolean clientSideDistinctFiltering, Boolean clientSidePaging, UniqueList1 stringCache, Dictionary2 typeConvertersToRun, IRetrievalQuery queryExecuted) at SD.LLBLGen.Pro.ORMSupportClasses.DaoBase.GetAsProjection(List1 valueProjectors, IGeneralDataProjector projector, ITransaction transactionToUse, IRetrievalQuery queryToExecute, Dictionary2 typeConvertersToRun) at SD.LLBLGen.Pro.ORMSupportClasses.DaoBase.GetAsProjection(List1 valueProjectors, IGeneralDataProjector projector, ITransaction transactionToUse, QueryParameters parameters) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider.ExecuteValueListProjection(QueryExpression toExecute) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at UserQuery.RunUserAuthoredQuery() in \Temp\LINQPad_teadusfa\query_tgotih.cs:line 51

Am I making an error, is this supported by LlblGen or does LlblGen have a problem?

Regards, Alexander.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 06-Sep-2014 03:34:22   

I don't understand your 'select {...}'. What is exactly what you want to retrieve? What would be the approximate sql?

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 06-Sep-2014 10:35:03   

I think it's the joined results which you refer to as a single object (groupedProperties). That's not going to work. You can only refer to the real entities in the projection not the object created from the joined results as it doesn't know how to create that in the projection. I agree with David, it's odd that you use this, what is it that you want to do?

Frans Bouma | Lead developer LLBLGen Pro
AlexanderM
User
Posts: 41
Joined: 18-May-2012
# Posted on: 07-Sep-2014 13:41:54   

It's like an example from Jon Skeet's book C# in depth, which is LinqToSql:


from defect in context.Defects
join subscription in context.NotificationSubscriptions
on defect.Project equals subscription.Project
into groupedSubscriptions
select new {Defect = defect, Subscriptions = groupedSubscriptions}

The result you want is a list of Main Entities and with every Entity a list of Subentities. If there is another (nice) way of the same result, I'm also happy, but this seems like a standard way in Linq.

@David, the select creates an anonymous type. I also tried it with a class I created but also got same error. I tried many ways but didn't get any result. The expected SQL will be complex, Jon Skeet calls it "hectic" simple_smile

@Otis: I thought I have seen a forum post from you where it worked on the Northwind database on friday morning. But I couldn't find it in the afternoon rage

Edit: I Remembered the History functionality of the browser and I was referring to this article http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=13932&HighLight=1 but that has a .Count() and I already found out that this was working during my tests. That's much easier in SQL.

AlexanderM
User
Posts: 41
Joined: 18-May-2012
# Posted on: 08-Sep-2014 09:54:48   

I have found another way of getting the result I want. Using the fact that with LlblGen you can access entities which are related by Foreign Keys. The generated SQL looks efficient so that's good.


    var poolEmps = (from pe in PoolEmployee 
    where pe.PoolId == 1783
    select new {PoolEmployee = pe, Properties = pe.PropertyOfEmployee}).ToList();

So I can proceed, but I would still like to discuss if LlblGen should be able to support the join into construct.

Btw: Jon Skeet has the SQL in his book, I guess you have his book, am I coorect? Or should I enter the SQL?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 08-Sep-2014 10:27:49   

AlexanderM wrote:

I have found another way of getting the result I want. Using the fact that with LlblGen you can access entities which are related by Foreign Keys. The generated SQL looks efficient so that's good.


    var poolEmps = (from pe in PoolEmployee 
    where pe.PoolId == 1783
    select new {PoolEmployee = pe, Properties = pe.PropertyOfEmployee}).ToList();

So I can proceed, but I would still like to discuss if LlblGen should be able to support the join into construct.

It does support the join into construct, it doesn't support the into result in the final projection. The main reason is that specifying entities in the projection requires special code as we have two pipelines for fetching: a projection system and an entity fetch system. So you can use join into without problems, the object you specify after 'into' can't appear in the projection, that's the only limitation simple_smile

Btw: Jon Skeet has the SQL in his book, I guess you have his book, am I coorect? Or should I enter the SQL?

I don't have that book, sorry. But your query can simply be specified as:


var poolEmps = (from pe in PoolEmployee 
    where pe.PoolId == 1783
    join poe in PropertyOfEmployee on pe.Id equals poe.PoolEmployeeId
    select new {PoolEmployee = pe, Properties = poe}).ToList();

the join into is mostly only used to specify 'DefaultIfEmpty' to specify left joins, for the rest you don't need it. Your workaround query will insert the join indirectly.

Frans Bouma | Lead developer LLBLGen Pro
AlexanderM
User
Posts: 41
Joined: 18-May-2012
# Posted on: 08-Sep-2014 11:19:22   

Hello,

But your query can simply be specified as:

Code:


var poolEmps = (from pe in PoolEmployee 
    where pe.PoolId == 1783
    join poe in PropertyOfEmployee on pe.Id equals poe.PoolEmployeeId
    select new {PoolEmployee = pe, Properties = poe}).ToList();

the join into is mostly only used to specify 'DefaultIfEmpty' to specify left joins, for the rest you don't need it. Your workaround query will insert the join indirectly.

This not the same as mine. Your query produces a normal inner join while a group join has all elements of the left sequence. I would call it an outer join but this page (http://msdn.microsoft.com/en-us/library/bb311040.aspx) says:

Therefore, the group join is still basically an inner-equijoin except that the result sequence is organized into groups.

This seems to work in Entity Framework see for instance: http://www.progware.org/Blog/post/Left-Outer-Join-in-LINQ-to-Entities-(for-Entity-Framework-4).aspx which also explains the differences in this kind of statements with or without defaultifempty (flattening).

Regards, Alexander.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 08-Sep-2014 12:49:45   

AlexanderM wrote:

Hello,

But your query can simply be specified as:

Code:


var poolEmps = (from pe in PoolEmployee 
    where pe.PoolId == 1783
    join poe in PropertyOfEmployee on pe.Id equals poe.PoolEmployeeId
    select new {PoolEmployee = pe, Properties = poe}).ToList();

the join into is mostly only used to specify 'DefaultIfEmpty' to specify left joins, for the rest you don't need it. Your workaround query will insert the join indirectly.

This not the same as mine. Your query produces a normal inner join while a group join has all elements of the left sequence. I would call it an outer join but this page (http://msdn.microsoft.com/en-us/library/bb311040.aspx) says:

Therefore, the group join is still basically an inner-equijoin except that the result sequence is organized into groups.

Ah indeed. I didn't look into the type of the poe element, my assumptions were wrong indeed. The thing is I have no idea how to fetch the data of the hierarchical graph group join results requires, other than generating a flat projection with all rows in place and then creating the hierarchy from the rows retrieved (which can lead to a lot of duplicates). Our pipeline doesn't support that kind of hierarchical fetching, it can't reconstruct a hierarchy from a flat set; we do the equivalent with nested queries which result in 1 query per node, but not based on 1 single query. Maybe we'll add it in the future, for now this isn't supported, as it in general leads to inefficient queries and there are more efficient ways to fetch hierarchies.

EF supports this as their way to fetch hierarchies is by projecting a flat set to a hierarchy, which in general is less efficient (hence why their Include() calls are slow, it uses the same projection).

So in short: we do support groupjoin (SelectMany()) but only if it's used further inside the query (e.g. as a source for further querying inside the query), not as an element in the projection. Thanks for clarifying simple_smile

Frans Bouma | Lead developer LLBLGen Pro
AlexanderM
User
Posts: 41
Joined: 18-May-2012
# Posted on: 08-Sep-2014 13:11:10   

OK, that's a clear answer. I understand why you didn't build it, the groupjoin doesn't have a translation into SQL and with a database with Foreign Keys it is probably not necessary to support this. Anyway I asked it because I couldn't find anything about it and was wondering if I was making a small mistake. The next person can maybe find this thread and knows that it is not supported so can stop trying.

Interesting to know that the construct can be used for further querying maybe I can use that in this case, because this part was just the beginning.