- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
FetchEntity and threads
Joined: 07-Jan-2007
Hello,
I have problems when calling FetchEntity (of the same entity) from several threads. Each thread uses his own DataAccessAdapter. The threads call FetchEntity of the same entity. The code created by llblgen or internal llblgen classes throw exceptions. Should I do special operation when working with threads?
Here come several stack traces (within llblgen code) of th exceptions I get:
case 1:
Stack Trace:
System.ArgumentException: Item has already been added. Key in dictionary: "8549a191-91b4-49a0-bb2e-462510df12c0" Key being added: "8549a191-91b4-49a0-bb2e-462510df12c0"
at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add)
at System.Collections.Hashtable.Add(Object key, Object value)
at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.SetEntitySyncInformation(String fieldName, IEntity2 relatedEntity, IEntityRelation relation)
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1539
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)
case 2:
Stack Trace:
System.NullReferenceException: Object reference not set to an instance of an object.
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.DesetupSyncEnlCommon(Boolean signalRelatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1500
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1531
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)
case 3:
Stack Trace:
System.NullReferenceException: Object reference not set to an instance of an object.
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1540
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)
case 4:
System.NullReferenceException: Object reference not set to an instance of an object.
at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.SetEntitySyncInformation(String fieldName, IEntity2 relatedEntity, IEntityRelation relation)
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1539
at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)
Where MB.DB.LLBLGen.Auto\DataStructures is code generated by llblgen for our database.
case 5:
Stack Trace:
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: Index was out of range. Must be non-negative and less than the size of the collection.
at System.Collections.CollectionBase.System.Collections.IList.get_Item(Int32 index)
at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2.get_Item(Int32 index)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, 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.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)
Thanks ,
Cylakes
Joined: 07-Jan-2007
Hello,
First, answers to the questions.
1) The version info llblgen version: version: 1.0.2004.2 final Released on June 13th,2005
The dlls: dll: SD.LLBLGen.Pro.ORMSupportClasses.NET11 Version: 1.0.2004.2
RunTime Version: v1.1.4322
dll: SD.LLBLGen.Pro.DQE.Oracle10g.NET11 Version : 1.0.2004.2 RunTime Version: v1.1.4322
2) The reason for trying to fetch the same entity from multiple threads: We implement web services . Each client of the web service may fetch the same entity. It may occur simultaniously. The client may fetch the same records in the tables or different records in the same tables. For example , we have EMPLOYEE table. Different web services (that run simultaniously) may fetch the same record in EMPLOYEE table or different records in EMPLOYEE table. Each web service is a different thread.
And now some more information : 1) We have quite a "healthy" prefetch path when calling FetchEntity. 2) When we enclose the FetchEntity in critical section (lock()) , no problems occur.
Thanks,
Cylakes
Could you please download the latest build of the 1.0.2004.2 runtime libs (and templates)? They're available in the customer area. If possible, please migrate to 1.0.2005.1 (which is a free upgrade for you).
Joined: 07-Jan-2007
Hello, We are at a final stage of system tests. It may be problematic for us to upgrade to new run time libraries or to new llblgen version (since we have to repeat all tests...) Will the upgrade of the run time libs fix the problem? Is it a known problem? Is the code generated by llblgen thread safe? Thanks, Cylakes
cylakes wrote:
Hello, We are at a final stage of system tests. It may be problematic for us to upgrade to new run time libraries or to new llblgen version (since we have to repeat all tests...) Will the upgrade of the run time libs fix the problem?
that's hard to tell, as there were a series of bugfixes to the 1.0.2004.2 runtimes after the date of your designer build. The 1.0.2004.2 build available on our website is usable in your current code. I'd also update the templates to the latest build.
Is it a known problem?
A null-ref exception can be caused by many things.
Is the code generated by llblgen thread safe? Thanks, Cylakes
No, only the singleton code is threadsafe (internally in the runtime lib) as there aren't any locks etc. used. So if you are setting and getting data from a single entity by multiple threads, you will run into problems unless you use a lock yourself to mark a critical section.
Joined: 07-Jan-2007
Hello, Maybe i did not explain myself well about the way we work with threads. Each thread has its own objects. The threads do not share the same entity objects. Here is a demo of our work for each thread:
using System;
using System.Data;
using System.Data.Common;
using System.ComponentModel;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using SD.LLBLGen.Pro.ORMSupportClasses;
using SD.LLBLGen.Pro.DQE.Oracle;
using MB.DB.LLBLGen.Auto; // classes generated by LLBLGEN
using MB.DB.LLBLGen.Auto.EntityClasses; // classes generated by LLBLGEN
using MB.DB.LLBLGen.Auto.FactoryClasses; // classes generated by LLBLGEN
using MB.DB.LLBLGen.Auto.HelperClasses; // classes generated by LLBLGEN
class ReadDBLogicManager
{
//-------------------------------------------------------------------
// This is the code skeleton that is performed by each thread.
// Each thread has its own DataAccessAdapter
// Each thread has it own EnlCommonEntity
// However, there may be situations that different threads access the same records in database.
// When there is no lock statement we have the mentioned problems in
// FetchEntity. When we add the lock,there aer no problems.
//-------------------------------------------------------------------
public void ThreadMain()
{
MB.DB.LLBLGen.Auto.DataAccessAdapter dataAcessAdapter;// inherits SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase
dataAcessAdapter = new MBDataAccessAdapter(connectionString, keepConnectionOpen);
MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity enlCommonEntity;// inherits SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2
enlCommonEntity = new enlCommonEntity(...);
IPrefetchPath2 prefetchPath = new PrefetchPath2((Int32)EntityType.EnlCommonEntity);
prefetchPath.Add(....);
prefetchPath.Add(....);
prefetchPath.Add(....);
Boolean doesExist;
// lock(typeof(ReadDBLogicManager))
// {
// Executing query
doesExist = dataAcessAdapter.FetchEntity(enlCommonEntity, prefetchPath);
// }
....
}
}
As I mentioned, in the code demo the lock solves the problems. Is working with several threads , where each thread has is own LLBLGEN generated objects, problematic? Should every call to llblgen objects be enclosed in lock statement?
Thanks, Cylakes
The code looks OK. I have no idea why this can cause problems, as all your code is contained inside a method, and by that the objects created are local to the method, and thus to the thread running the method, i.o.w.: objects created by thread A running ThreadMain aren't visible by thread B and vice versa.
What's also odd is that a GUID is already present in a set according to the exceptions, which is odd as a guid is always unique.
Could you at least TRY the latest runtime lib builds and the latest templates for 1.0.2004.2 ? (thus latest templates -> regenerate code). So we can rule out bugs which are fixed in later builds of 1.0.2004.2 ?
Also, as you're using multi-threading, please add the compiler directive 'MULTICPU' to the dbspecific C# project.
Joined: 07-Jan-2007
Hello, 1)
Also, as you're using multi-threading, please add the compiler directive 'MULTICPU' to the dbspecific C# project.
How do I do it?
2)
Assuming that regenerating LLBLGEN does not help, is there any other alternatives?
Thanks, Cylakes
Joined: 14-Jan-2007
I would also like to get a list of common LLBL actions which are not threadsafe. Especially DataAccessAdapter, FetchEntity, FetchEntityCollection... Etc.
For example: "Using FetchEntity on a single cpu, single user, multi threaded application is not safe"
Thanks, Roy
cylakes wrote:
Hello, 1)
Also, as you're using multi-threading, please add the compiler directive 'MULTICPU' to the dbspecific C# project.
How do I do it?
in the vs.net C# project properties.
2)
Assuming that regenerating LLBLGEN does not help, is there any other alternatives? Thanks, Cylakes
Well, I have no other idea why it fails. Our code is used in a lot of webapps for example, which are multi-threaded by default, so it's not something that is caused by a fundamental flaw.
RoyRichter wrote:
I would also like to get a list of common LLBL actions which are not threadsafe. Especially DataAccessAdapter, FetchEntity, FetchEntityCollection... Etc.
For example: "Using FetchEntity on a single cpu, single user, multi threaded application is not safe" Thanks, Roy
DataAccessAdapter instances aren't thread safe. So per thread you need to create a NEW instance. Entity instances are as any other object: if you share them among threads, you have to do the locking yourself.