- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Error when using batching on ODP.NET version 4.122.23.1
Joined: 25-Sep-2025
Hello LLBLGen team,
We're running into an error when using a batch size of 2 or higher with Oracle Data Provider version 4.122.23.1. Batching with this version of ODP.NET creates an actionQuery with various null properties (including Command.CommandText) that results in a null reference exception when the command text is fetched. Using a batch size of 0 or 1 prevents the error, and using ODP.NET version 4.122.21.1 also prevents the error. Trying to use the ORM profiler resulted in the same error at get_CommandText, so the error causing queries aren't logged. ODP.NET package versions 23.8.0 and 23.9.1 both cause the error.
Could the source of this issue be an incompatibility of our version of LLBLGen (5.8.3) with the latest ODP.NET?
Below are the exception message, stack trace, and version numbers. Thanks for your help!
The Import has been terminated at 09/25/2025 13:01:04 UTC.
Reason: ProductEmployeeImportManager_ProcessUserChunk ThreadNumber: 1 The following exception occurred at System.String get_CommandText():
Object reference not set to an instance of an object.
Stack Trace:
at Oracle.ManagedDataAccess.Client.OracleCommand.get_CommandText()
at Grb.Platform.Framework.Business.Lower.DatabaseCommon.DataAccessAdapterUtility.GetCommandText(IActionQuery actionQuery) in C:\Source\Production\Platform\Framework\Business.Lower\DatabaseCommon\DataAccessAdapterUtility.cs:line 113
at Grb.Platform.Framework.Business.Lower.DatabaseCommon.DataAccessAdapterUtility.OnSaveEntityComplete(IActionQuery saveQuery, IEntity2 entityToSave, RecoveryStrategyBase activeRecoveryStrategy) in C:\Source\Production\Platform\Framework\Business.Lower\DatabaseCommon\DataAccessAdapterUtility.cs:line 52
at Grb.Platform.Framework.Business.Lower.Oracle.DatabaseSpecific.DataAccessAdapter.OnSaveEntityComplete(IActionQuery saveQuery, IEntity2 entityToSave) in C:\Source\Production\Platform\Framework\Business.Lower\Oracle (ODP.NET)\DatabaseSpecific\CustomDataAccessAdapterCode.cs:line 45
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.<PersistQueue>b__57_1(IActionQuery q, EntityBase2 e)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.PerformPostPersistenceQueryExecuted(PackedActionQuery packedQuery, Int32 amountSaved)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.PerformPostActionQueryWork(Int32 resultActionQuery, PackedActionQuery packedQuery)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.ExecuteElements(List`1 elementsToRun)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.Execute(ActionQueueElement`1 actionQueueElement, IActionQuery query, Type typeOfNextElement)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.PersistQueue(List`1 queueToPersist, Boolean insertActions)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.SaveEntity(IEntity2 entityToSave, Boolean refetchAfterSave, IPredicateExpression updateRestriction, Boolean recurse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.<>c__DisplayClass19_0.<SaveEntity>b__0()
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteWithActiveRecoveryStrategy[T](Func`1 toExecute)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.SaveEntity(IEntity2 entityToSave, Boolean refetchAfterSave, IPredicateExpression updateRestriction, Boolean recurse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.SaveEntity(IEntity2 entityToSave, Boolean refetchAfterSave, Boolean recurse)
at Grb.Platform.Business.HR.BusinessManager.SetEmployeeEntity_SaveChildren(EmpGeneralEntity employeeEntity, IDataAccessAdapter externalAdapter, Boolean reFetchAfterSave) in C:\Source\Production\Platform\Business\HR\BusinessManager.vb:line 11123
The error occuring on a retry:
The following exception occurred at System.String get_CommandText():
Object reference not set to an instance of an object.
Stack Trace:
at Oracle.ManagedDataAccess.Client.OracleCommand.get_CommandText()
at Grb.Platform.Framework.Business.Lower.DatabaseCommon.DataAccessAdapterUtility.GetCommandText(IActionQuery actionQuery) in C:\Source\Production\Platform\Framework\Business.Lower\DatabaseCommon\DataAccessAdapterUtility.cs:line 113
at Grb.Platform.Framework.Business.Lower.DatabaseCommon.DataAccessAdapterUtility.OnSaveEntityComplete(IActionQuery saveQuery, IEntity2 entityToSave, RecoveryStrategyBase activeRecoveryStrategy) in C:\Source\Production\Platform\Framework\Business.Lower\DatabaseCommon\DataAccessAdapterUtility.cs:line 52
at Grb.Platform.Framework.Business.Lower.Oracle.DatabaseSpecific.DataAccessAdapter.OnSaveEntityComplete(IActionQuery saveQuery, IEntity2 entityToSave) in C:\Source\Production\Platform\Framework\Business.Lower\Oracle (ODP.NET)\DatabaseSpecific\CustomDataAccessAdapterCode.cs:line 45
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.<PersistQueue>b__57_1(IActionQuery q, EntityBase2 e)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.PerformPostPersistenceQueryExecuted(PackedActionQuery packedQuery, Int32 amountSaved)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.PerformPostActionQueryWork(Int32 resultActionQuery, PackedActionQuery packedQuery)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.ExecuteElements(List`1 elementsToRun)
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQueryController.Execute(ActionQueueElement`1 actionQueueElement, IActionQuery query, Type typeOfNextElement)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.PersistQueue(List`1 queueToPersist, Boolean insertActions)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.SaveEntity(IEntity2 entityToSave, Boolean refetchAfterSave, IPredicateExpression updateRestriction, Boolean recurse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.<>c__DisplayClass19_0.<SaveEntity>b__0()
at SD.LLBLGen.Pro.ORMSupportClasses.RecoveryStrategyBase.Execute[TResult](Func`1 toExecute)
LLBLGen Pro version + buildnr: 5.8.3
Runtime library version: 5.8.3
.NET version: .NET Framework 4.7.2
Database Version: Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production
Data Provider: Oracle.ManagedDataAccess version 4.122.23.1, packages versions 23.8.0 and 23.9.1
I can't reproduce it on .net 6 with 23.9.1 on oracle 12c, using LLBLGen Pro v5.12
(Queries from ormprofiler) Inserts:
BEGIN
SELECT SCOTT.SEQ_DEP.NEXTVAL
INTO NULL /* :p2 */
FROM DUAL;
INSERT INTO SCOTT.DEPT
(DNAME,
DEPTNO,
LOC)
VALUES ('NewDep' /* :p1 */,
NULL /* :p2 */,
'foo' /* :p3 */);
SELECT SCOTT.SEQ_DEP.NEXTVAL
INTO NULL /* :p5 */
FROM DUAL;
INSERT INTO SCOTT.DEPT
(DNAME,
DEPTNO,
LOC)
VALUES ('NewDep2' /* :p4 */,
NULL /* :p5 */,
'Bar' /* :p6 */);
SELECT SCOTT.SEQ_DEP.NEXTVAL
INTO NULL /* :p8 */
FROM DUAL;
INSERT INTO SCOTT.DEPT
(DNAME,
DEPTNO,
LOC)
VALUES ('NewDep3' /* :p7 */,
NULL /* :p8 */,
'Bar2' /* :p9 */);
END;
updates:
BEGIN
0 /* :pLLBLROWCOUNT */ := 0;
UPDATE SCOTT.DEPT
SET DNAME = 'NewDepx' /* :p1 */
WHERE (SCOTT.DEPT.DEPTNO = 44 /* :p2 */);
0 /* :pLLBLROWCOUNT */ := 0 /* :pLLBLROWCOUNT */ + SQL%ROWCOUNT;
UPDATE SCOTT.DEPT
SET DNAME = 'NewDep2x' /* :p3 */
WHERE (SCOTT.DEPT.DEPTNO = 45 /* :p4 */);
0 /* :pLLBLROWCOUNT */ := 0 /* :pLLBLROWCOUNT */ + SQL%ROWCOUNT;
UPDATE SCOTT.DEPT
SET DNAME = 'NewDep3x' /* :p5 */
WHERE (SCOTT.DEPT.DEPTNO = 46 /* :p6 */);
0 /* :pLLBLROWCOUNT */ := 0 /* :pLLBLROWCOUNT */ + SQL%ROWCOUNT;
END;
As the crash happens in your code, please check if something else is wrong, and if the queries are ran without you obtaining the query from it. (you can see that for instance by enabling tracing).
The main idea is that the IActionQuery instances are packed in a packed query and then, after the batch has run, they're passed to the OnSaveEntityComplete method. As the queries on oracle are disposed, it might be they're no longer available, but that's just a wild guess. I couldn't find an issue being fixed related to this after 5.8.3 so I don't think that's it either.
Joined: 25-Sep-2025
Thank you for your previous response. I've had a chance to look into the issue a bit more, and found the cause to be that the IActionQuery passed into OnSaveEntityComplete has already been disposed. The dispose appears to happen in CreatePackedQueryFromCollectedElements. Is this what you refered to when you said "the queries on oracle are disposed"?
The IActionQuery passed to OnSaveEntityComplete is also disposed when using the 2021 version of ODP.NET, but the properties of Command like CommandText are still available. When using the 2023 version of ODP.NET, CommandText and other properties are null. Is this the expected behavior, and the IActionQuery passed to OnSaveEntityComplete can't have it's CommandText and other Command properties read on the latest ODP.NET when using batching?
Thanks for your help!
As the command has been completed, it's indeed no longer available at that stage. it's sadly a side effect of ODP.NET which requires these early disposes. What is your use case for this requirement? As there are other ways to obtain the query