|
|
dvdwouwe
User
Location: Belgium
Joined on: 24-Jul-2009 19:19:52
Posted: 56 posts
|
We have a piece of code that fetches an object graph from the database. It has been used already millions of times. Today however, we got an error with the following stack trace.
| Code: |
msg: Object reference not set to an instance of an object. Stack: at SD.LLBLGen.Pro.ORMSupportClasses.PersistenceCore.CalculateHashForEntityBasedOnRelation(IEntityCore entity, IEntityRelation relation, Boolean forPkSide, Boolean& hashRejected) at SD.LLBLGen.Pro.ORMSupportClasses.PersistenceCore.MergeNormal(IEntityCollectionCore rootEntities, IPrefetchPathElementCore currentElement, Boolean rootEntitiesArePkSide) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath, Boolean forceParameterizedPPath) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchAdditionalPrefetchPath(IPrefetchPath2 prefetchPath, Context contextToUse, IEntity2 fetchedEntity, IRelationPredicateBucket filterToUse) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityUsingFilter(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse, IRelationPredicateBucket filter, ExcludeIncludeFieldsList excludedIncludedFields) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse, ExcludeIncludeFieldsList excludedIncludedFields) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath) at KSZServices.ORM.FetchMessage(Int64 MessageID)
|
We reviewed our own code and didn't find any errors. Moreover it would seem a bit weird since the code has been running fine for awhile. In addition, we ran the code (after adding extra logging) again on the data that gave problems and this time the code worked just fine.
Any hints on what might have gone wrong here?
|
|
|
Otis
LLBLGen Pro Team
Location: The Hague, The Netherlands
Joined on: 17-Aug-2003 18:00:36
Posted: 27585 posts
|
Could you run the code which crashes with the debug build of the ORMSupportclasses dll ? This debug build is in the runtimelibraries folder. It's the same code built at the same time but with debug switch on, and a pdb file, so we can get a line number with the crash which will make it easier to track down what might went wrong.
|
|
|
dvdwouwe
User
Location: Belgium
Joined on: 24-Jul-2009 19:19:52
Posted: 56 posts
|
|
Sadly, we cannot reproduce the issue. This code is running on a production machine. After we encountered the error, we put extra logging in our code, and ran the code again. This time, however, the code executed successfully on the same data.
|
|
|
MTrinder
Support Team
Location: London by day, Milton Keynes by night.
Joined on: 08-Oct-2008 17:55:47
Posted: 994 posts
|
Is the DataAccessAdapter shared across threads at all?
Matt
|
|
|
dvdwouwe
User
Location: Belgium
Joined on: 24-Jul-2009 19:19:52
Posted: 56 posts
|
|
No, the DataAccessAdapter is not shared acroos threads. We have several threads running in parallel, but each thread allocates its own DataAccessAdapter.
|
|
|
Walaa
Support Team
Location: Egypt
Joined on: 21-Aug-2005 16:03:48
Posted: 9817 posts
|
Neither do you share entities between these threads?
|
|
|
dvdwouwe
User
Location: Belgium
Joined on: 24-Jul-2009 19:19:52
Posted: 56 posts
|
|
No, the threads work on different sets of tables.
|
|
|
Walaa
Support Team
Location: Egypt
Joined on: 21-Aug-2005 16:03:48
Posted: 9817 posts
|
Could you please specify which runtime library version/build number are you using?
|
|
|
Otis
LLBLGen Pro Team
Location: The Hague, The Netherlands
Joined on: 17-Aug-2003 18:00:36
Posted: 27585 posts
|
The method which crashes, really only allows a nullref to happen if the fields in a relationship are not there or missing, e.g. it's really odd, and likely a shared prefetch path object or relationship shared among threads, but as you say nothing is shared among threads...
It still might be a sharing of elements between threads, so please check that.
|
|
|
dvdwouwe
User
Location: Belgium
Joined on: 24-Jul-2009 19:19:52
Posted: 56 posts
|
Otis,
It is this code that is running in a WCF service, hosted in an NT service on a Windows 2008 R2 OS that is virtualized.
| Code: |
private static PrefetchPath2 RRBISRootNode; private static PrefetchPath2 GetRRBISRootNode() { // Object graph voor RRBISMessage // (*) Leaf nodes // //Citizen // | // +-- StandardAddress // | | // | +-- Municipality (*) // | | // | +-- Street (*) // | | // | +-- Country (*) // | // +-- PlainAddress // | | // | +-- Country (*) // | // +-- Location (*) // | // +-- Description (*) // | // +-- ManagerLocation (*) // | // +-- CivilState (*)
if (RRBISRootNode == null) {
// Level 1 RRBISRootNode = new PrefetchPath2(EntityType.StgRRBISCitizenEntity); IPrefetchPathElement2 StandardAddressNode = RRBISRootNode.Add(StgRRBISCitizenEntity.PrefetchPathStgRRBISStandardAddress); IPrefetchPathElement2 PlainAddressNode = RRBISRootNode.Add(StgRRBISCitizenEntity.PrefetchPathStgRRBISPlainAddress); RRBISRootNode.Add(StgRRBISCitizenEntity.PrefetchPathStgRRBISLocation); RRBISRootNode.Add(StgRRBISCitizenEntity.PrefetchPathStgRRBISDescription); RRBISRootNode.Add(StgRRBISCitizenEntity.PrefetchPathStgRRBISManagerLocation); RRBISRootNode.Add(StgRRBISCitizenEntity.PrefetchPathStgRRBISCivilState);
// Level 2 StandardAddressNode.SubPath.Add(StgRRBISStandardAddressEntity.PrefetchPathStgRRBISCountry); StandardAddressNode.SubPath.Add(StgRRBISStandardAddressEntity.PrefetchPathStgRRBISMunicipality); StandardAddressNode.SubPath.Add(StgRRBISStandardAddressEntity.PrefetchPathStgRRBISStreet); PlainAddressNode.SubPath.Add(StgRRBISPlainAddressEntity.PrefetchPathStgRRBISCountry); } return RRBISRootNode; } public static StgRRBISCitizenEntity FetchRRBISMessage(long RRBISCitizenID) { using (DataAccessAdapter Adapter = ORM.DataAccesAdapterFactory()) { StgRRBISCitizenEntity Citizen = new StgRRBISCitizenEntity(RRBISCitizenID); Adapter.FetchEntity(Citizen, GetRRBISRootNode()); return Citizen; } }
|
When we fetched a graph using FetchRRBISMessage we got the error. But the strange thing is after restarting we didn't got it back. The service was running for weeks fine and processed thousands of graphs.
The method that uses the method above is:
| Code: |
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public sealed class ExportXml : IExportXml { }
|
The service contract is very simple:
| Code: |
[ServiceContract( SessionMode = SessionMode.Required, Namespace = "http://debouw.be/CrossDB/IExportXml")] public interface IExportXml { [OperationContract] ExportXMLFileResult ExportMessages(XmlMessageType XmlMessageType, XmlExportType XmlExportType, string ExportPath);
[OperationContract] XElement ExportMessage(XmlMessageType XmlMessageType, long MessageID); }
|
Now, I m wondering if using a static is the prefered way here? Because the graph is fetched a lot of times during one service call i used this technique to avoid the cost of building this PrefetchPath again and again. Or is het much better to use the TLS for this?
Danny
|
|
|
MTrinder
Support Team
Location: London by day, Milton Keynes by night.
Joined on: 08-Oct-2008 17:55:47
Posted: 994 posts
|
Static methods are not inherently thread-safe - it may be worth moving these methods to an actual instance class.
Matt
|
|
|
Otis
LLBLGen Pro Team
Location: The Hague, The Netherlands
Joined on: 17-Aug-2003 18:00:36
Posted: 27585 posts
|
Static variables mean that if the objects they refer to change internally, it's not thread safe. Static methods can be thread safe if they create objects locally and not store them outside themselves (like you do).
A prefetch path contains in a node the data it fetches. So a node is fetched, the data fetched is stored in the EntityCollection in that node and therefore if you share that path among threads, it goes wrong.
So a static method which creates the path for you and returns it to the caller, no problem, as the object is created locally, however storing it and re-using that static object is a problem. So I'd refactor the code in such a way that the path is created every time (which takes almost no time) in a static method.
|
|
|
|