Threading and async FetchEntityCollection

Posts   
 
    
Posts: 20
Joined: 02-Oct-2006
# Posted on: 05-Jan-2007 20:54:33   

I am trying to in the background start async threads to load collections that relate to an entity and we are using LLBLgen 2.0 Adapter in VS 2005 C# with SQl 2005 back end.

I am sharing an adapter but I have set "MultipleActiveResultSets=True" for the SQL 2005 connection (and making an adapter for each thread didn't fix the issue I am having).

I have created an AbstractEntity parent class that glues the atomized parameters for each entity into collections that can be handled as arrays and handled generically accross entities.

I am calling BackgroundPreCacheFor to start the process.

when I actually need the collection immediately I am calling StartFetchCollection with isBackground set to false. (and it works fine)


    /// <summary>
    /// object to pass to FetchCollection as need to send multiple data points and only one parameter can be sent
    /// </summary>
    class FetchCollectionParams
    {
        public IEntityCollection2 collection;
        public IRelationPredicateBucket filter;
        public IPrefetchPath2 prefetchPath;
    }

    public static void BackgroundPreCacheFor(AbstractEntity parent)
    {
        foreach (KeyValuePair<string, IEntityCollection2> kvp in parent.EntityCollections)
            StartFetchCollection(parent, kvp.Key, true);    
    }

    public static void StartFetchCollection(AbstractEntity parent, string collectionName, bool isBackground)
    {
    
        bool newThread = false;
        lock (parent.ThreadsGettingCollections)
            if (!parent.ThreadsGettingCollections.ContainsKey(collectionName))
            {//add new thread
                if (isBackground)
                    parent.ThreadsGettingCollections.Add(collectionName, new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(FetchCollection)));
                else
                    parent.ThreadsGettingCollections.Add(collectionName, null);

                newThread = true;
            }

        System.Threading.Thread thread = parent.ThreadsGettingCollections[collectionName];

        if (newThread )
        {
            FetchCollectionParams fetch = new FetchCollectionParams();
            fetch.collection = parent.EntityCollections[collectionName];
            fetch.filter = parent.GetRelationInfo()[collectionName];
            fetch.prefetchPath = new PrefetchPath2(parent.LLBLGenProEntityTypeValue);
            fetch.prefetchPath.Add(parent.PrefetchPath[collectionName]);

            if (isBackground)
            {
                thread.IsBackground = isBackground;
                thread.Start(fetch);
            }
            else
            {
                FetchCollection(fetch);
            }
            
        }
        else if (thread != null && thread.IsBackground && !isBackground)
        {
            thread.IsBackground = isBackground;
        }

        if (!isBackground && thread != null && thread.IsAlive)
        {
            thread.Join();
        }
    }

    /// <summary>
    /// fetch collection
    /// </summary>
    /// <param name="fetchobject">object of type FetchEntityCollectionParams (can't send typed parameters to a background process)</param>
    public static void FetchCollection(object fetchobject)
    {
        //DataAccessAdapter adapter = new DataAccessAdapter(); //covers over global
        FetchCollectionParams fetch = (FetchCollectionParams)fetchobject;
        adapter.FetchEntityCollection(fetch.collection, fetch.filter, fetch.prefetchPath);
        //adapter.CloseConnection();
    }

and I get the following errors alternatively at random on the line

adapter.FetchEntityCollection(collection, filter, prefetchPath);

System.NullReferenceException was unhandled
  Message="Object reference not set to an instance of an object."
  Source="SD.LLBLGen.Pro.ORMSupportClasses.NET20"
  StackTrace:
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateSubPathFilterParameterizedPPath(IEntityCollection2 rootEntities, IPrefetchPath2 prefetchPath, Boolean& rootEntitiesArePkSide, IPrefetchPathElement2 currentElement, IRelationPredicateBucket elementFilter, Int32& amountRootEntitiesUsable)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchParameterisedPrefetchPath(IEntityCollection2 rootEntities, Int64 maxNumberOfItemsToReturn, IPrefetchPath2 prefetchPath)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityCollection(IEntityCollection2 collectionToFill, IRelationPredicateBucket filterBucket, IPrefetchPath2 prefetchPath)
       at DataLayer.FetchCollection(Object fetchobject) in c:\Northwind-Adapter-two\Web\App_Code\DataLayer.cs:line 975
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart(Object obj)


SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException was unhandled
  Message="An exception was caught during the execution of a retrieval query: Invalid operation. The connection is closed.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception."
  Source="SD.LLBLGen.Pro.ORMSupportClasses.NET20"
  RuntimeBuild="09192006"
  RuntimeVersion="2.0.0.0"
  QueryExecuted="\r\n\tQuery: SELECT [Northwind].[dbo].[Employees].[EmployeeID] AS [EmployeeId], [Northwind].[dbo].[Employees].[LastName], [Northwind].[dbo].[Employees].[FirstName], [Northwind].[dbo].[Employees].[Title], [Northwind].[dbo].[Employees].[TitleOfCourtesy], [Northwind].[dbo].[Employees].[BirthDate], [Northwind].[dbo].[Employees].[HireDate], [Northwind].[dbo].[Employees].[Address], [Northwind].[dbo].[Employees].[City], [Northwind].[dbo].[Employees].[Region], [Northwind].[dbo].[Employees].[PostalCode], [Northwind].[dbo].[Employees].[Country], [Northwind].[dbo].[Employees].[HomePhone], [Northwind].[dbo].[Employees].[Extension], [Northwind].[dbo].[Employees].[Photo], [Northwind].[dbo].[Employees].[Notes], [Northwind].[dbo].[Employees].[ReportsTo], [Northwind].[dbo].[Employees].[PhotoPath] FROM [Northwind].[dbo].[Employees]  WHERE ( ( ( ( [Northwind].[dbo].[Employees].[EmployeeID] = @EmployeeId1))))\r\n\tParameter: @EmployeeId1 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 5.\r\n"
  StackTrace:
       at SD.LLBLGen.Pro.ORMSupportClasses.RetrievalQuery.Execute(CommandBehavior behavior)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteMultiRowRetrievalQuery(IRetrievalQuery queryToExecute, IEntityFactory2 entityFactory, IEntityCollection2 collectionToFill, IFieldPersistenceInfo[] fieldsPersistenceInfo, Boolean allowDuplicates, IEntityFields2 fieldsUsedForQuery)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityCollectionInternal(IEntityCollection2 collectionToFill, IRelationPredicateBucket& filterBucket, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, Int32 pageNumber, Int32 pageSize)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityCollection(IEntityCollection2 collectionToFill, IRelationPredicateBucket filterBucket, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, Int32 pageNumber, Int32 pageSize)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchParameterisedPrefetchPath(IEntityCollection2 rootEntities, Int64 maxNumberOfItemsToReturn, IPrefetchPath2 prefetchPath)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityCollection(IEntityCollection2 collectionToFill, IRelationPredicateBucket filterBucket, IPrefetchPath2 prefetchPath)
       at DataLayer.FetchCollection(Object fetchobject) in c:\Northwind-Adapter-two\Web\App_Code\DataLayer.cs:line 975
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart(Object obj)

from the errors I am wondering if the LLBL is behind the scenes trying to do the same thing ... because I seem to be catching things in weird states.

or if your doing threading behind the scenes as well (for other reasons) and the priority I am executing at is causing problems relative to the priority of LLBL threads.

or you see something i am missing...

Thanks!

mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 06-Jan-2007 12:09:33   

Hi there,

Few thoughts of mine.

  1. create an adapter per thread - this is the most probable cause for error 2. Don't use MARS. I don't think LLBLGenPro (guessing) handles MARS correctly automatically. Perhaps if you pass a valid connection and instructs adapter not to close it.

  2. Why don't you check the call stack - seems like one of the parameters is null. Which one?

  3. I wouldn't do multithreading like you are doing it. Mostly because if your object is databound you'll have problems. Then you don't gain anything if you use a thread for collection, actually you slow down the process. A better option would be to create a background thread and load everything you need there - in this single thread.

  4. Pieces of code are missing in your example (AbstractEntity?).

Chester
Support Team
Posts: 223
Joined: 15-Jul-2005
# Posted on: 07-Jan-2007 19:10:32   

From the docs:

Note: The DataAccessAdapter class is not thread safe and should not be used as such. Each thread should use its own instance, it's not safe to share a DataAccessAdapter instance among multiple threads.

Posts: 20
Joined: 02-Oct-2006
# Posted on: 09-Jan-2007 17:34:27   

I have tried it with an adapter for each thread ... ( you can see it commented out in the code above)

I get the same errors:


System.NullReferenceException was unhandled
  Message="Object reference not set to an instance of an object."
  Source="SD.LLBLGen.Pro.ORMSupportClasses.NET20"
  StackTrace:
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateSubPathFilterParameterizedPPath(IEntityCollection2 rootEntities, IPrefetchPath2 prefetchPath, Boolean& rootEntitiesArePkSide, IPrefetchPathElement2 currentElement, IRelationPredicateBucket elementFilter, Int32& amountRootEntitiesUsable)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchParameterisedPrefetchPath(IEntityCollection2 rootEntities, Int64 maxNumberOfItemsToReturn, IPrefetchPath2 prefetchPath)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
       at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityCollection(IEntityCollection2 collectionToFill, IRelationPredicateBucket filterBucket, IPrefetchPath2 prefetchPath)
       at DataLayer.FetchCollection(Object fetchobject) in c:\Northwind-Adapter-two\Web\App_Code\DataLayer.cs:line 988
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart(Object obj)


and you can see the call stack shows in the errors ... it goes into the black box of llbl code

anyways i found my problem it wasn't with threading, and this code above is mostly fine I just can't retrieve many to many and many to one relationships this way .. it should only be doing one to many and one to one