- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Query spec with subquery using CorrelatedOver
Joined: 02-May-2014
Hello,
I'd like to create a subquery to retrieve child elements with multi predicate. Here is my code :
var qf = new QueryFactory();
var q = qf.Create()
.Select
(() => new BrokerTradeItem
{
equityAccountNumber = EquityAccountFields.AccountNumber.Trim().ToValue<string>(),
equityAccountLabel = EquityAccountFields.AccountLabel.Trim().ToValue<string>(),
isin = TradeslibFields.Isin.Trim().ToValue<string>(),
stockshortname = TradeslibFields.Stockshortname.Trim().ToValue<string>(),
side = TradeslibFields.Side.Trim().ToValue<string>(),
broker = EquityAccountExtClientRefFields.ExternalClientReference.Trim().ToValue<string>(),
quantity = TradeslibFields.Quantity.Sum().As("qty").ToValue<decimal>(),
averagePrice = ((TradeslibFields.AveragePrice * TradeslibFields.Quantity).Sum().ToValue<decimal>() / TradeslibFields.Quantity.Sum().ToValue<decimal>()),
dealDate = TradeslibFields.DealDate.ToValue<DateTime>(),
tradeSlibRegistrationEntities =
new BindingList<TradeSlibRegistrationEntity>
(
qf.TradeSlibRegistration
.CorrelatedOver
(
TradeslibFields.DealDate.Equal(TradeSlibRegistrationFields.DealDate)
.And(TradeslibFields.Isin.Trim().Equal(TradeSlibRegistrationFields.Isin.Trim()))
.And(TradeslibFields.Side.Trim().NotEqual(TradeSlibRegistrationFields.Side.Trim()))
.And(EquityAccountExtClientRefFields.ExternalClientReference.Trim().Equal(TradeSlibRegistrationFields.BrokerCode.Trim()))
.And(TradeSlibRegistrationFields.Cancelled == false)
)
.ToResultset<TradeSlibRegistrationEntity>()
)
}
)
.From
(
qf.Tradeslib
.InnerJoin(TradeslibEntity.Relations.EquityAccountEntityUsingEquityAccountOid)
.InnerJoin(EquityAccountEntity.Relations.EquityAccountExtClientRefEntityUsingEquityAccount)
)
.GroupBy
(
EquityAccountFields.AccountNumber,
EquityAccountFields.AccountLabel,
TradeslibFields.DealDate,
TradeslibFields.Isin,
TradeslibFields.Stockshortname,
TradeslibFields.Side,
EquityAccountExtClientRefFields.ExternalClientReference,
TradeslibFields.DealDate
)
.Where
(
(TradeslibFields.Cancelled.Equal(false))
);
adapter.FetchQuery(q);
I obtain an internal error, but I don't understand why. If I use one condition it works. This part is the problem :
tradeSlibRegistrationEntities =
new BindingList<TradeSlibRegistrationEntity>
(
qf.TradeSlibRegistration
.CorrelatedOver
(
TradeslibFields.DealDate.Equal(TradeSlibRegistrationFields.DealDate)
.And(TradeslibFields.Isin.Trim().Equal(TradeSlibRegistrationFields.Isin.Trim()))
.And(TradeslibFields.Side.Trim().NotEqual(TradeSlibRegistrationFields.Side.Trim()))
.And(EquityAccountExtClientRefFields.ExternalClientReference.Trim().Equal(TradeSlibRegistrationFields.BrokerCode.Trim()))
.And(TradeSlibRegistrationFields.Cancelled == false)
)
.ToResultset<TradeSlibRegistrationEntity>()
)
Could you help me ? Thanks, Etienne.
Using llblgen pro 4.1 Final, Framework .Net 4.5 (VS2012) and SqlServer 2012
Joined: 02-May-2014
Hi Otis,
Sorry, here is the error :
Message : Internal error. InnerException : null RuntimeBuild : 03272014 RuntimeVersion : 4.1.0.0 Source : SD.LLBLGen.Pro.ORMSupportClasses StackTrace :
at SD.LLBLGen.Pro.QuerySpec.FunctionMappingExpression.SD.LLBLGen.Pro.ORMSupportClasses.IExpression.get_LeftOperand() in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\BuildingBlocks\FunctionMappingExpression.cs:line 207 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.Traverse(IExpression objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 382 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.Traverse(IEntityFieldCore field) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 188 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.TraversePredicateField(IEntityFieldCore field, String objectAlias) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 272 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.TraversePredicate(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 245 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 218 at SD.LLBLGen.Pro.QuerySpec.PredicateFinder.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\Visitors\PredicateFinder.cs:line 81 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 205 at SD.LLBLGen.Pro.QuerySpec.PredicateFinder.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\Visitors\PredicateFinder.cs:line 81 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 205 at SD.LLBLGen.Pro.QuerySpec.PredicateFinder.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\Visitors\PredicateFinder.cs:line 81 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 205 at SD.LLBLGen.Pro.QuerySpec.PredicateFinder.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\Visitors\PredicateFinder.cs:line 81 at SD.LLBLGen.Pro.ORMSupportClasses.QueryApiObjectTraverser.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\GraphTraversers\QueryApiObjectTraverser.cs:line 205 at SD.LLBLGen.Pro.QuerySpec.PredicateFinder.Traverse(IPredicate objectToTraverse) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\Visitors\PredicateFinder.cs:line 81 at SD.LLBLGen.Pro.QuerySpec.QuerySpec.GetCorrelationPredicatesForNestedQuery() in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\BuildingBlocks\QuerySpec.cs:line 684 at SD.LLBLGen.Pro.QuerySpec.Projection.WrapRawElementsIfNecessary() in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\BuildingBlocks\Projection.cs:line 388 at SD.LLBLGen.Pro.QuerySpec.Projection.Preprocess(FunctionMappingStore customFunctionMappings, FunctionMappingStore dqeFunctionMappings) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\BuildingBlocks\Projection.cs:line 214 at SD.LLBLGen.Pro.QuerySpec.DynamicQuery.Preprocess(FunctionMappingStore dqeFunctionMappings) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\BuildingBlocks\DynamicQuery.cs:line 205 at SD.LLBLGen.Pro.QuerySpec.DynamicQuery.PrepareForExecution(FunctionMappingStore dqeMappings) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\BuildingBlocks\DynamicQuery.cs:line 323 at SD.LLBLGen.Pro.QuerySpec.Adapter.AdapterExtensionMethods.FetchQuery[TElement](IDataAccessAdapter adapter, DynamicQuery
1 query) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v4.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\QuerySpec\AdapterSpecific\AdapterExtensionMethods.cs:line 239 at AtlasModuleSpanishRegistration.DataLoaderTools.GetBrokerTradeData(AdapterTools
1 adapterTools, DateTime tradeDateMin, DateTime tradeDateMax, String side, String isin) in c:\_Dev\Projects\Atlas\trunk\Modules\AtlasModuleSpanishRegistration\Tools\DataLoaderTools.cs:line 251 at AtlasModuleSpanishRegistration.DataLoaderTools.GetBrokerTradeData(AdapterTools`1 adapterTools, DateTime tradeDate) in c:\_Dev\Projects\Atlas\trunk\Modules\AtlasModuleSpanishRegistration\Tools\DataLoaderTools.cs:line 132 at AtlasModuleSpanishRegistration.XtraUcSpanishRegTradeSlibRegistrationView.LoadBrokerTrade(DateTime tradeDate) in c:\_Dev\Projects\Atlas\trunk\Modules\AtlasModuleSpanishRegistration\Form\XtraUcSpanishRegTradeSlibRegistrationView.cs:line 110 at AtlasModuleSpanishRegistration.XtraUcSpanishRegTradeSlibRegistrationView.SearchTrade(DateTime tradeDate) in c:\_Dev\Projects\Atlas\trunk\Modules\AtlasModuleSpanishRegistration\Form\XtraUcSpanishRegTradeSlibRegistrationView.cs:line 78
Thanks, Etienne
thanks. With 'one condition' you mean:
this works: .CorrelatedOver ( TradeslibFields.DealDate.Equal(TradeSlibRegistrationFields.DealDate) )
but all the conditions it doesn't work?
Joined: 02-May-2014
Yes, that's what I think at the beginning, but I found the reason why it doesn't work.
If I remove the .Trim() on all fields, it works. Is there a reason why it can't be used ?
Also the filter on Cancelled == false have to be done in the where clause
Here is the corrected code :
tradeSlibRegistrationEntities =
new BindingList<TradeSlibRegistrationEntity>
(
qf.TradeSlibRegistration
.CorrelatedOver
(
TradeslibFields.DealDate.Equal(TradeSlibRegistrationFields.DealDate)
.And(TradeslibFields.Isin.Equal(TradeSlibRegistrationFields.Isin))
.And(TradeslibFields.Side.NotEqual(TradeSlibRegistrationFields.Side))
.And(EquityAccountExtClientRefFields.ExternalClientReference.Equal(TradeSlibRegistrationFields.BrokerCode))
)
.Where
(
TradeSlibRegistrationFields.Cancelled.Equal(false)
)
.ToResultset<TradeSlibRegistrationEntity>()
)
Joined: 02-May-2014
I also found a strange thing : If I use the following code :
tradeSlibRegistrationEntities =
new BindingList<TradeSlibRegistrationEntity>
(
qf.TradeSlibRegistration
.CorrelatedOver
(
TradeslibFields.DealDate.Equal(TradeSlibRegistrationFields.DealDate)
.And(TradeslibFields.Isin.Equal(TradeSlibRegistrationFields.Isin))
.And(TradeslibFields.Side.Equal(TradeSlibRegistrationFields.Side))
.And(EquityAccountExtClientRefFields.ExternalClientReference.Equal(TradeSlibRegistrationFields.BrokerCode))
)
.Where
(
TradeSlibRegistrationFields.Cancelled.Equal(false)
)
.ToResultset<TradeSlibRegistrationEntity>()
)
Or
tradeSlibRegistrationEntities =
new BindingList<TradeSlibRegistrationEntity>
(
qf.TradeSlibRegistration
.CorrelatedOver
(
TradeslibFields.DealDate.Equal(TradeSlibRegistrationFields.DealDate)
.And(TradeslibFields.Isin.Equal(TradeSlibRegistrationFields.Isin))
.And(TradeslibFields.Side.NotEqual(TradeSlibRegistrationFields.Side))
.And(EquityAccountExtClientRefFields.ExternalClientReference.Equal(TradeSlibRegistrationFields.BrokerCode))
)
.Where
(
TradeSlibRegistrationFields.Cancelled.Equal(false)
)
.ToResultset<TradeSlibRegistrationEntity>()
)
the result is the same (I change TradeslibFields.Side.Equal into TradeslibFields.Side.NotEqual ==> It always use the Equal condition, the NotEqual doesn't seems to work.
Do you know why ?
EtienneKepler wrote:
Yes, that's what I think at the beginning, but I found the reason why it doesn't work.
If I remove the .Trim() on all fields, it works. Is there a reason why it can't be used ?
The Trim() function is executed in the database, so it can't find a correlation between the outcome of the function and the related element, as it will build a comparer based on the predicate specified: as one side produces a value in the DB, it can't determine what the real outcome is, as it compares field values: it calculates hashes which are compared. The nested query is merged in-memory using these hashes, so if one side's value is to be computed again by a function, which executes in the DB, it's impossible to find a correlation.
(I hope that makes sense, if not please let me know )
Also the filter on Cancelled == false have to be done in the where clause
Yes, because the correlatedover predicate expression is solely used to build comparers so a parent (the outer query element) can be found for a child (nested query element) using value hashes which are calculated from each sides' fields as specified in the predicate expression.
The cancelled == false predicate has no play in that correlation (as it's a filter on the elements, not a correlation definition, i.e. which child belongs to which parent) so it has to be specified in the where clause.
the result is the same (I change TradeslibFields.Side.Equal into TradeslibFields.Side.NotEqual ==> It always use the Equal condition, the NotEqual doesn't seems to work.
Do you know why ?
are both fields null by any chance? As this is tied to data and your query is rather big, could you create a repro case for this with just the query which fails and e.g. on Adventureworks or northwind so no databases have to be distributed? Thanks
Joined: 02-May-2014
Hi Otis,
Thanks for the explanation, it's now clear to me. For the last part, the field is nullable in one table and not nullable in the other table, but in both case they are filled.
I've made an easier sample. Here are the 2 tables :
CREATE TABLE [dbo].[TableTestCase1]( [ID] [int] IDENTITY(1,1) NOT NULL, [ISIN] nchar NULL, [side] nchar NULL, [quantity] [int] NOT NULL, CONSTRAINT [PK_TableTestCase1] PRIMARY KEY CLUSTERED ([ID] ASC)) ON [PRIMARY]
CREATE TABLE [dbo].[TableTestCase2]( [ID] [int] IDENTITY(1,1) NOT NULL, [ISIN] nchar NULL, [side] nchar NULL, [broker] nchar NOT NULL, CONSTRAINT [PK_TableTestCase2] PRIMARY KEY CLUSTERED ([ID] ASC)) ON [PRIMARY]
insert into TableTestCase1 values ('FR001', 'A', 10) insert into TableTestCase1 values ('FR001', 'V', 20)
insert into TableTestCase2 values ('FR001', 'A', 'BuySide') insert into TableTestCase2 values ('FR001', 'V', 'SellSide')
And here is the code :
private class Test
{
public string broker { get; set; }
public BindingList<TableTestCase1Entity> listTest { get; set; }
}
private void TestMethods()
{
using (var adapter = this._adapterTools.GetNewAdapter()) // This create a new adapter
{
var qf = new QueryFactory();
var q = qf.Create()
.Select
(() => new Test
{
broker = TableTestCase2Fields.Broker.Source("tt").ToValue<string>(),
listTest =
new BindingList<TableTestCase1Entity>
(
qf.TableTestCase1
.CorrelatedOver
(
TableTestCase1Fields.Isin.Equal(TableTestCase2Fields.Isin.Source("tt"))
.And(TableTestCase1Fields.Side.Equal(TableTestCase2Fields.Side.Source("tt")))
)
.ToResultset<TableTestCase1Entity>()
)
}
)
.From
(
qf.TableTestCase2.As("tt")
);
var q2 = qf.Create()
.Select
(() => new Test
{
broker = TableTestCase2Fields.Broker.Source("tt").ToValue<string>(),
listTest =
new BindingList<TableTestCase1Entity>
(
qf.TableTestCase1
.CorrelatedOver
(
TableTestCase1Fields.Isin.Equal(TableTestCase2Fields.Isin.Source("tt"))
.And(TableTestCase1Fields.Side.NotEqual(TableTestCase2Fields.Side.Source("tt")))
)
.ToResultset<TableTestCase1Entity>()
)
}
)
.From
(
qf.TableTestCase2.As("tt")
);
var list1 = adapter.FetchQuery(q);
var list2 = adapter.FetchQuery(q2);
}
}
When I run this, list1 and list2 are equals, but they shouldn't. The property listTest should be filled differently.
I hope this informations are sufficient
Hmm, I have to bring the sad news that the system currently only supports 'equal' predicates, as in: which values in parent have to be equal to which values in child to have a match. It ignored operands. It generates at runtime in Projection.PostProcessNestedQueries a lambda with the body containing a series of:
(parentValues, childValues) => FieldUtilities.ValuesAreEqual(parentValues[parentFieldIndex], childValues[childFieldIndex])
lambda's which it then compiles and runs over the elements.
I could adjust this a bit by adding a Not if the operand in the predicate is NotEqual instead of Equal, which would make your queries work like they should.
I'll also look into the operators between the predicate expressions, as a 'And Not' or 'Or' also has to be taken into account (now, they're anded together, which might not be what you specified). The code is a port from our linq provider's nested query system which only works with anded expressions and comparisons which are always using Equal, as there's no way to specify the correlation differently in Linq, but in Queryspec there is.
Hopefully I have a fixed runtime tomorrow (Friday) or else on Monday.
Joined: 02-May-2014
Thanks Otis,
I've done a workaround for now, I removed the condition and filtered data after the query execution. It loads more data, but it works , waiting for a fix.
Regards, Etienne.
It turns out a little different than I described earlier, and it can't be fixed. The hashcodes of both the child row fields in the correlation predicate and the parent row fields in the correlation predicate are calculated. These hashes are combined from the hashcodes of the various fields, so if there are multiple fields in the parent in the predicate, then for each field a hashcode is calculated and these hashcodes are then combined into a single code (as a string, as that's the easiest here, which always works, hashcodes are ints, and the code is easy to find back in a lookup hashset).
For each child the hashcode for the fields in the correlation predicate for the child are calculated, and then looked up in the hashes for the parent. If there's a match, the child will be merged into the parent row.
The correlation predicate is used for the nested query to limit the data set returned, and when there's a hashcollision, i.e. multiple rows have the same hashvalue but in theory can have different values. If that's the case, the compiled correlation predicate is used to find the right row in the list of rows which have the same hashvalue.
This system is very efficient, but has a drawback: the operators in the predicate can't be anything else than Equal: in the case of NotEqual, the hashcode isn't matching, as the hashcode is simply created from the value. The parent row isn't found based on using the predicate, but through hashes as it would otherwise be way too slow (n*m). With hashes it's OK.
Therefore I can't fix it... What I thought yesterday looked great on paper, I forgot that the merging was done by hash comparisons. This morning I already thought it couldn't work and indeed, testing it and reviewing the code underlined that. Sorry.
I've added an exception throw to the process method to signal unsupported operator usage.
Joined: 02-May-2014
Hi Otis,
Thanks for you investigation, I will use the workaround so
In this case, I have only 2 possible values, maybe I can use the where clause, I will try it later.
Regards, Etienne.