- Home
- LLBLGen Pro
- Bugs & Issues
linqMetaData throws exception, possibly due to subsequent outer joins
Joined: 26-Jan-2006
Hi...
I am using the LinqMetaData object to execute LINQ queries in LLBLGen and have encountered a reproducible bug using the latest 2.6 Oct 6th release.
I believe the problem occurs when the linq expression calls for an outer join, followed by a subsequent outer join.
A few months ago, I encountered a similar problem like this which is documented in this thread:
http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=13521
While that issue was since resolved, it appears a similar issue is causing the exception I am now experiencing.
To produce this, I have created a table called asset, with a nullable FK relation to a table called work_package on asset_id. To filter records correctly, we need an additional relation to a table called deficiency on work_package_id (this relation is not nullable, but both joins must be outer).
The linq expression desired looks like this:
[Test()]
public void TestLLBLGenBugWithNewVersionOct6_Test2()
{
IDataAccessAdapter adapter = DataAccessAdapterFactory.CreateStandardAdapter();
LinqMetaData linqMetaData = new LinqMetaData(adapter);
var query = from a in linqMetaData.Asset
join wp in linqMetaData.WorkPackage on a.AssetId equals wp.AssetId into workPackages
from workPackage in workPackages.DefaultIfEmpty()
join d in linqMetaData.Deficiency on workPackage.WorkPackageId equals d.WorkPackageId into deficiencies
from deficiency in deficiencies.DefaultIfEmpty()
select new WorkPackage
{
AssetId = a.AssetId,
AssetName = a.Name,
AssetNumber = a.AssetNumber.ToString(),
AssetLetter = a.AssetLetter,
};
this._workPackageList = new List<WorkPackage>(query.ToArray());
}
}
The exception raised is this:
Value cannot be null.
Parameter name: key
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: key
Source Error:
Line 2077: {
Line 2078: IEntityFactory2 toReturn = null;
Line 2079: _factoryPerType.TryGetValue(typeOfEntity, out toReturn);
Line 2080: return toReturn;
Line 2081: }
Source File: C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.DataEntities.Generic\FactoryClasses\EntityFactories.cs Line: 2079
Stack Trace:
[ArgumentNullException: Value cannot be null.
Parameter name: key]
System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) +41
System.Collections.Generic.Dictionary`2.FindEntry(TKey key) +2667593
System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value) +14
Tec.TecAms.DataEntities.Generic.FactoryClasses.EntityFactoryFactory.GetFactory(Type typeOfEntity) in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.DataEntities.Generic\FactoryClasses\EntityFactories.cs:2079
Tec.TecAms.DataEntities.Generic.FactoryClasses.ElementCreator.GetFactoryImpl(Type typeOfEntity) in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.DataEntities.Generic\FactoryClasses\EntityFactories.cs:2184
Tec.TecAms.DataEntities.Generic.FactoryClasses.ElementCreator.GetFactory(Type typeOfEntity) in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.DataEntities.Generic\FactoryClasses\EntityFactories.cs:2109
SD.LLBLGen.Pro.LinqSupportClasses.LinqUtils.CreateEntityInstanceFromEntityType(Type entityType, IElementCreatorCore generatedCodeElementCreator) +108
SD.LLBLGen.Pro.LinqSupportClasses.LinqUtils.GetEntityName(Object value, IElementCreatorCore generatedCodeElementCreator) +58
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.CreateDynamicRelation(Object left, Object right, JoinHint joinType, String aliasLeft, String aliasRight, IPredicate onClause) +67
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleJoinExpression(JoinExpression expressionToHandle) +4047
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) +837
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) +187
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleGroupJoinExpression(GroupJoinExpression expressionToHandle) +42
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleGroupJoinExpression(GroupJoinExpression expressionToHandle) +35
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) +702
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) +187
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleAndProcessJoinExpressionSide(SetExpression side, Expression sideSelector, Expression& handledSide, Expression& handledSideSelector, String& aliasSide) +26
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleJoinExpression(JoinExpression expressionToHandle) +836
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) +837
SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) +187
SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.HandleExpressionTree(Expression expression) +872
SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression) +10
SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression) +14
SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +36
System.Linq.Buffer`1..ctor(IEnumerable`1 source) +239
System.Linq.Enumerable.ToArray(IEnumerable`1 source) +79
Tec.TecAms.ReportServer.WorkPackageReport.FillReportData() in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.ReportServer\WorkPackageReport.cs:62
Tec.TecAms.UI.Web.Secure.Reports.WorkPackageReport.FillReportData() in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.UI.Web\Secure\Reports\WorkPackageReport.aspx.cs:76
Tec.TecAms.UI.Web.Secure.Reports.WorkPackageReport.Setup() in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.UI.Web\Secure\Reports\WorkPackageReport.aspx.cs:62
Tec.TecAms.UI.Web.Secure.Reports.WorkPackageReport.Page_Load(Object sender, EventArgs e) in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.UI.Web\Secure\Reports\WorkPackageReport.aspx.cs:86
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +15
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +33
System.Web.UI.Control.OnLoad(EventArgs e) +99
GrowingTechnologies.Web.ControlLibrary.Custom.BasePage.OnLoad(EventArgs e) +44
Tec.TecAms.UI.Web.BasePage.OnLoad(EventArgs e) in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.UI.Web\BasePages\BasePage.cs:131
Tec.TecAms.UI.Web.BasePages.BaseSecurePage.OnLoad(EventArgs e) in C:\Projects\GT_Clients\TEC\TecAms\Code\Tec.TecAms\Tec.TecAms.UI.Web\BasePages\BaseSecurePage.cs:144
System.Web.UI.Control.LoadRecursive() +47
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1436
Version Information: Microsoft .NET Framework Version:2.0.50727.1433; ASP.NET Version:2.0.50727.1433
Removing the subsequent outer join provides a successful test.
Thanks for your attention in this!
Joined: 17-Aug-2003
I saw a similar issue a couple of days ago, and indeed it looks like an error, though with that previous issue the query was really massive so debugging was really impossible...
With this smaller query, I'll see if I can reproduce it. Navigating nullable FK's (order.Customer.CustomerId for example if order.CustomerID is nullable) do result in left joins btw, which might help writing a workaround query.
Will look into it.
(btw, it has nothing to do with that other issue you mentioned, it's related to the fact that two groupjoins are placed after eachother, and the engine has to relate relation 2 to relation 1 and can't find a proper element. )
The joins in your query are of no use, as no filter is placed on the joined elements and as they're left joins, they won't filter any Assert rows. The second DefaultIfEmpty is of no use as well, because the FK is not nullable, so a LEFT join will result in the same set as an INNER join. That might help rewriting the query to a working one which doesn't fail with the current code.
Joined: 17-Aug-2003
Reproduced on northwind. Looking into it.
[Test]
public void MultiDefaultIfEmptyInLineWithGroupJoins()
{
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
LinqMetaData metaData = new LinqMetaData(adapter);
var q = from o in metaData.Order
join e in metaData.Employee on o.EmployeeId equals e.EmployeeId into employees
from emp in employees.DefaultIfEmpty()
join et in metaData.EmployeeTerritory on emp.EmployeeId equals et.EmployeeId into empterritories
from empterritory in empterritories.DefaultIfEmpty()
select new
{
o.CustomerId,
o.OrderId,
o.OrderDate
};
foreach(var v in q)
{
}
}
}
Joined: 17-Aug-2003
I managed to fix this issue. I don't know if further, more complex defaultifempty trees will work but they're in almost all cases rewritable to more efficient queries.
(see attachment)
Joined: 26-Jan-2006
Thanks, that seemed to have fixed it!!!
As for your comments, I will experiment with removing the subsequent DefaultIfEmpty joins and see if the output query is desirable, if it is, then thanks for the good tip! And thanks for the prompt response.
Joined: 17-Aug-2003
Isz wrote:
Thanks, that seemed to have fixed it!!!
As for your comments, I will experiment with removing the subsequent DefaultIfEmpty joins and see if the output query is desirable, if it is, then thanks for the good tip! And thanks for the prompt response.
In general you should see querying as this process: - I want rows of type A - If I want a subset of all rows of A, I have to filter A by defining the filter in such a way that it represents the aspects (!) of the subset you want and have to specify the filter accordingly, starting from A. - Joins serve filters and selects. This means that if you need to filter on a related entity, you use joins (directly or indirectly), if you need data from a related entity, you join, but in all other cases joins are not needed (with the small addition: an inner join can be used as a filter)
This sounds as old news, but often this is overlooked . Your query is a good example (no offence): you want Assets, but there's no real subset defined. This means that you don't need to apply any filter and therefore also no joins at all.