DataAccessAdapter.FetchEntityCollection returns ArgumentNullException on Oracle CLOB data type

Posts   
 
    
brentpahl
User
Posts: 1
Joined: 11-Mar-2021
# Posted on: 11-Mar-2021 10:21:43   

I ran a simple FetchEntityCollection on a table to get all entities, using the DataAccessAdapter. No RelationPredicateBucket and no PrefetchPath (i.e. both are null). This triggered the following System.ArgumentNullException : 'Value cannot be null. Parameter name: value'

Stack Trace:

   at System.BitConverter.ToString(Byte[] value, Int32 startIndex, Int32 length)
   at OracleInternal.TTC.TTCLob.GetLobIdString(Byte[] lobLocator)
   at OracleInternal.ServiceObjects.OracleDataReaderImpl.CollectTempLOBsToBeFreed(Int32 rowNumber)
   at Oracle.ManagedDataAccess.Client.OracleDataReader.ProcessAnyTempLOBs(Int32 rowNumber)
   at Oracle.ManagedDataAccess.Client.OracleDataReader.Read()
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityMaterializerBase.Materialize(Func`4 valueReadErrorHandler, String& failureErrorText)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.ExecuteMultiRowRetrievalQuery(IRetrievalQuery queryToExecute, IEntityFactory2 entityFactory, IEntityCollection2 collectionToFill, IFieldPersistenceInfo[] fieldsPersistenceInfo, Boolean allowDuplicates, IEntityFields2 fieldsUsedForQuery)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchEntityCollectionInternal(QueryParameters parameters)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchEntityCollection(QueryParameters parameters)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.<>c__DisplayClass10_0.<FetchEntityCollection>b__0()
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteWithActiveRecoveryStrategy(Action toExecute)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityCollection(QueryParameters parameters)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchEntityCollection(IEntityCollection2 collectionToFill, IRelationPredicateBucket filterBucket, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList excludedIncludedFields, Int32 pageNumber, Int32 pageSize)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchEntityCollection(IEntityCollection2 collectionToFill, IRelationPredicateBucket filterBucket, IPrefetchPath2 prefetchPath)
Bookkeeping:
LLBLGen Pro Editor v5.8.0
SD.LLBLGen.Pro.DQE.OracleODPNET v5.8.0
SD.LLBLGen.Pro.ORMSupportClasses v5.8.0
Oracle Database 12cR2 64-bit
Oracle.ManagedDataAccess v12.2.1100
.NET Framework 4.7.2

The Oracle table entities we are retrieving have two CLOB fields. Because the error occurred while trying to convert a Byte Array to a String, I believe the CLOB fields are responsible for the ArgumentNullException. We call this method from a Service we created:

public IEnumerable<T> FetchEntityCollection(RelationPredicateBucket predicateBucket = null, PrefetchPath2 preFetchPath = null) {
    using (var adapter = new DataAccessAdapter(_connectionString)) {
        var data = new EntityCollection<T>();
        adapter.FetchEntityCollection(data, predicateBucket, preFetchPath);
        return data;
    }
}

The table has no foreign keys. It has a compound primary key (two fields, one Varchar2 and one Date). The entity definition was created directly from the database table, using the LLBLGen Pro reverse-engineering feature. It seems like I'm running a fairly straightforward use case, so I'm surprised that it is crashing.

I did try changing the entity data type for the CLOBS from String to Byte[], but it still threw an exception.

Any help you could provide would be appreciated.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 11-Mar-2021 11:03:58   

As the crash happens in the Oracle ADO.NET provider, there's little we can do: it happens during the read action of the datareader, as you can see from the stacktrace. From the looks of it, they pass a null value to the bitconverter while they should have tested for that. Oracle has released newer ODP.NET versions than the one you're using tho, could you try a newer one from nuget to see if the problem has been fixed in their provider?

I see in the 19.0.0 version they do a null check at least: (decompiled). I added a comment below in the code


internal void CollectTempLOBsToBeFreed(int rowNumber, ArrayList tempLOBArrayList)
{
    if (ProviderConfig.m_bTraceLevelPrivate)
    {
        Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)262400, OracleTraceClassName.OracleDataReaderImpl, OracleTraceFuncName.CollectTempLOBsToBeFreed);
    }
    if (m_accessors != null)
    {
        for (int i = 0; i < m_accessors.Length; i++)
        {
            if (!(m_accessors[i] is TTCLobAccessor))
            {
                continue;
            }
            TTCLobAccessor tTCLobAccessor = m_accessors[i] as TTCLobAccessor;
            if (tTCLobAccessor.AbstractOrTempLOB(rowNumber))
            {
                byte[] lobLocator = tTCLobAccessor.GetLobLocator(rowNumber);
                if (lobLocator == null)            // <<<<<<<<<<<< This one is likely not present in 12.2
                {
                    continue;
                }
                if (m_connectionImpl.TemporaryLobReferenceGet(TTCLob.GetLobIdString(lobLocator)) == null)
                {
                    if (tTCLobAccessor.m_definedColumnType == OraType.ORA_OCICLobLocator)
                    {
                        tempLOBArrayList.Add(new OracleClobImpl(m_connectionImpl, lobLocator, bNClob: false));
                    }
                    else if (tTCLobAccessor.m_definedColumnType == OraType.ORA_OCIBLobLocator)
                    {
                        tempLOBArrayList.Add(new OracleBlobImpl(m_connectionImpl, lobLocator, bCaching: false));
                    }
                }
                tTCLobAccessor.m_lobLocators[rowNumber].Clear();
            }
            else if (tTCLobAccessor.m_lobLocators[rowNumber] != null && tTCLobAccessor.m_lobLocators[rowNumber].Count > 0)
            {
                tTCLobAccessor.m_lobLocators[rowNumber].Clear();
            }
        }
    }
    if (ProviderConfig.m_bTraceLevelPrivate)
    {
        Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)262656, OracleTraceClassName.OracleDataReaderImpl, OracleTraceFuncName.CollectTempLOBsToBeFreed);
    }
}
Frans Bouma | Lead developer LLBLGen Pro