From: Richard B. <rb...@us...> - 2005-02-02 05:08:49
|
Update of /cvsroot/jcframework/dotnet In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22365 Modified Files: CCacheEntry.vb CConnection.vb CPersistenceBroker.vb readme.html Log Message: Fixes to restore state of cached objects and collections after transaction rollback Index: readme.html =================================================================== RCS file: /cvsroot/jcframework/dotnet/readme.html,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- readme.html 31 Jan 2005 06:17:39 -0000 1.6 +++ readme.html 2 Feb 2005 05:08:38 -0000 1.7 @@ -179,6 +179,11 @@ <td class="col1">Problem saving with 3 or more levels of interface inheritance</td> <td>Only the lowest and highest mapped interfaces were being saved when an object with 3+ levels of interface inheritances was persisted.<td> </tr> + <tr valign="top"> + <td>1113504</td> + <td class="col1">Collections not restored after transaction rollback</td> + <td>Collections are now cloned or copied at the start of a transaction. If the transaction aborts the copied collections are restored to the object.<td> + </tr> <tr class="tableCaption" valign="bottom"> <td colspan=3><br/>Modifications</td> </tr> Index: CConnection.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CConnection.vb,v retrieving revision 1.31 retrieving revision 1.32 diff -u -d -r1.31 -r1.32 --- CConnection.vb 31 Jan 2005 06:17:39 -0000 1.31 +++ CConnection.vb 2 Feb 2005 05:08:37 -0000 1.32 @@ -386,6 +386,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -616,6 +617,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction(isolationLevel) + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -845,6 +847,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -1096,6 +1099,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction(isolationLevel) + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -1325,6 +1329,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -1352,6 +1357,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction(isolationLevel) + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -1782,6 +1788,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -2010,6 +2017,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction(isolationLevel) + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -2229,6 +2237,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True @@ -2461,6 +2470,7 @@ If Me.ManageTransactions Then If Not Me.Started Then m_transaction = m_connection.BeginTransaction(isolationLevel) + getPersistenceBrokerInstance().startCacheTransaction(Me.Database) End If m_transactioncalls += 1 Me.Started = True Index: CPersistenceBroker.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CPersistenceBroker.vb,v retrieving revision 1.88 retrieving revision 1.89 diff -u -d -r1.88 -r1.89 --- CPersistenceBroker.vb 2 Jan 2005 09:41:10 -0000 1.88 +++ CPersistenceBroker.vb 2 Feb 2005 05:08:37 -0000 1.89 @@ -201,7 +201,7 @@ If Not x Is Nothing Then Throw x End If - End Try + End Try End SyncLock End Function @@ -701,8 +701,6 @@ Dim found As Boolean Dim col As IList Dim myKeys(cm.AssociationMaps.Count) As String - Dim tmpObj As IPersistableObject - cm.AssociationMaps.Keys.CopyTo(myKeys, 0) Dim anObjPers As IPersistableObject For i = 0 To cm.AssociationMaps.Count - 1 @@ -753,12 +751,12 @@ End Try End If If found Then - Value.IsDirty = False 'After populating a new object - Value.OriginalCacheKey = New CCacheKey(Value) - obj.SetAttributeValue(udaMap.Target, Value.GetSourceObject) - End If + Value.IsDirty = False 'After populating a new object + Value.OriginalCacheKey = New CCacheKey(Value) + obj.SetAttributeValue(udaMap.Target, Value.GetSourceObject) End If End If + End If ElseIf udaMap.Cardinality = CUDAMap.CardinalityEnum.ONE_TO_MANY Then For j = 1 To udaMap.getSize ValueVar = cm.getValueForRelationalDatabase(obj.GetValueByAttribute(udaMap.getEntry(j).FromAttrMap.Name)) @@ -781,26 +779,14 @@ cursor.loadObject(anObjPers.GetSourceObject) retrieveAssociations(anObjPers, conn, anObjPers.GetClassMap, useCache) End If - 'Need to determine if new object is already in the collection - '(prevents duplicates when multiple one-to-many associations exist) - gotValue = False - For Each tmpColObj As Object In col - If tmpColObj.GetType.IsSubclassOf(GetType(CPersistentObject)) Then - tmpObj = tmpColObj - Else - tmpObj = LocateOrCreateInjObject(tmpColObj) - End If - If tmpObj.Equals(anObjPers) Then - gotValue = True - End If - Next - - If Not gotValue Then - anObjPers.IsDirty = False 'After populating a new object - anObjPers.OriginalCacheKey = New CCacheKey(anObjPers) - col.Add(anObjPers.GetSourceObject) - m_cache.Add(anObjPers) 'Add retrieved objects to the cache - End If + anObjPers.IsDirty = False 'After populating a new object + anObjPers.OriginalCacheKey = New CCacheKey(anObjPers) + col.Add(anObjPers.GetSourceObject) + m_cache.Add(anObjPers) 'Add retrieved objects to the cache + Try + PCCacheSize.RawValue = m_cache.Count + Catch + End Try cursor.nextCursor() End While End If @@ -990,7 +976,7 @@ Try deletePrivateObject(obj, conn, deleteSuperClass) conn.commit() - Catch ex As Exception + Catch ex As Exception x = New DeleteException(ex.Message, ex) conn.rollback() Finally @@ -1005,7 +991,7 @@ If Not x Is Nothing Then Throw x End If - End Try + End Try End SyncLock End Sub @@ -1836,9 +1822,9 @@ Return cursor Catch - firstClassMap.RelationalDatabase.freeConnection(conn) - Return Nothing - End Try + firstClassMap.RelationalDatabase.freeConnection(conn) + Return Nothing + End Try End Function @@ -2605,6 +2591,10 @@ RaiseEvent LoginDetailsNeeded(sender, User, Password) End Sub + Friend Sub startCacheTransaction(ByVal reldb As _CRelationalDatabase) + m_cache.StartTransaction(reldb) + End Sub + Friend Sub commitCache(ByVal reldb As _CRelationalDatabase) m_cache.CommitChanges(reldb) End Sub Index: CCacheEntry.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CCacheEntry.vb,v retrieving revision 1.24 retrieving revision 1.25 diff -u -d -r1.24 -r1.25 --- CCacheEntry.vb 31 Jan 2005 22:29:06 -0000 1.24 +++ CCacheEntry.vb 2 Feb 2005 05:08:37 -0000 1.25 @@ -33,7 +33,7 @@ Private m_transactionType As CCacheEntry.CacheTransaction Private m_expiryTime As Date Private m_originalObject As IPersistableObject - Private m_collectionCollection As System.Collections.Specialized.StringDictionary + Private m_collectionCollection As Specialized.HybridDictionary '''----------------------------------------------------------------------------- ''' <summary> @@ -123,119 +123,139 @@ End Sub Public Sub CopyCollections() - Dim t, t_coll As Type - Dim prop, props() As PropertyInfo - - m_collectionCollection = New Specialized.StringDictionary - t_coll = GetType(CollectionBase) - t = m_object.GetObjectType - props = t.GetProperties - For Each prop In props - If prop.PropertyType Is t_coll OrElse prop.PropertyType.IsSubclassOf(t_coll) Then - 'We must precopy this collection into the collection copy - m_collectionCollection.Add(prop.Name, m_object.GetValueByAttribute(prop.Name)) - End If - Next - End Sub + Dim t, iEnumerableType, iListType, iDicType As Type Dim coll, obj As Object + Dim il As IList + Dim id As IDictionary + Dim f, fields() As FieldInfo + Dim value As Object - Public Sub RestoreCollections() - Dim t, t_coll As Type - Dim prop, props() As PropertyInfo - - t_coll = GetType(CollectionBase) - t = m_object.GetObjectType - props = t.GetProperties - For Each prop In props - If prop.PropertyType Is t_coll OrElse prop.PropertyType.IsSubclassOf(t_coll) Then - 'We must restore this collection from the collection copy - m_object.SetAttributeValue(prop.Name, m_collectionCollection.Item(prop.Name)) - End If - Next - End Sub + 'We must precopy this collection into the collection copy 'A simple assignment would just copy a reference to the colletion, while 'we need to copy the collection itself, so that adding/removing elements 'of the original won't effect the copy + m_collectionCollection = New Specialized.HybridDictionary t = m_object.GetObjectType + fields = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) + For Each f In fields + iListType = f.FieldType.GetInterface("IList", True) + iDicType = f.FieldType.GetInterface("IDictionary", True) + 'iEnumerableType = f.FieldType.GetInterface("IEnumerable", True) + If Not iListType Is Nothing OrElse Not iDicType Is Nothing Then + Dim ICloneType As Type = f.FieldType.GetInterface("ICloneable", True) + If Not ICloneType Is Nothing Then + 'Getting the ICloneable interface from the object. + Dim IClone As ICloneable = CType(f.GetValue(m_object), ICloneable) + 'We use the clone method to set the new value to the field. + coll = IClone.Clone() + Else + 'If the field doesn't support the ICloneable interface then just set it. + coll = Activator.CreateInstance(f.FieldType) 'need to copy references one-by-one + If Not iListType Is Nothing Then + il = CType(coll, IList) + For Each obj In f.GetValue(m_object) + il.Add(obj) + Next + Else + id = CType(coll, IDictionary) + For Each de As DictionaryEntry In f.GetValue(m_object) + id.Add(de.Key, de.Value) + Next + End If + End If + m_collectionCollection.Add(f.Name, coll) End If Next + End Sub - Public Sub New(ByVal lifetime As Double) - m_expiryTime = DateAdd(DateInterval.Minute, lifetime, Now) - End Sub + Public Sub RestoreCollections() + Dim t, iEnumerableType, iListType, iDicType As Type Dim f, fields() As FieldInfo + t = m_object.GetObjectType + fields = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) + For Each f In fields + iListType = f.FieldType.GetInterface("IList", True) + iDicType = f.FieldType.GetInterface("IDictionary", True) + 'iEnumerableType = f.FieldType.GetInterface("IEnumerable", True) + If Not iListType Is Nothing OrElse Not iDicType Is Nothing Then + 'We must restore this collection from the collection copy 'Just use a straight value assignment - no need to worry about cloning f.SetValue(m_object, m_collectionCollection.Item(f.Name)) End If Next + End Sub - Friend Sub resetExpiry(ByVal lifetime As Double) - m_expiryTime = DateAdd(DateInterval.Minute, lifetime, Now) - End Sub + Public Sub New(ByVal lifetime As Double) + m_expiryTime = DateAdd(DateInterval.Minute, lifetime, Now) + End Sub - Public Overrides Function ToString() As String - Dim s As String - Dim i As Short - Dim indent As Integer - Dim formatString As String + Friend Sub resetExpiry(ByVal lifetime As Double) + m_expiryTime = DateAdd(DateInterval.Minute, lifetime, Now) + End Sub - Dim cm As CClassMap - Dim am As CAttributeMap - Dim x As Object - Dim y As Object - Dim xstring As String - Dim ystring As String + Public Overrides Function ToString() As String + Dim s As String + Dim i As Short + Dim indent As Integer + Dim formatString As String - If m_object Is Nothing Then - Return "No persistent object" - End If + Dim cm As CClassMap + Dim am As CAttributeMap + Dim x As Object + Dim y As Object + Dim xstring As String + Dim ystring As String - s = m_object.GetObjectType.Name & ": " - indent = s.Length - If m_object.IsDirty Then - s &= "Dirty" - Else - s &= "Not Dirty" - End If - If m_object.Persistent Then - s &= ", Persistent" - Else - s &= ", NonPersistent" - End If - If m_object.IsLoading Then - s &= ", Loading" - Else - s &= ", Loaded" - End If - If m_object.AssociationsLoaded Then - s &= ", Assoc. Loaded" - Else - s &= ", No Assoc. Loaded" - End If - s &= vbCrLf - formatString = Space(indent) & "{0,-9}" & vbTab & "{1,-13}" & vbTab & "{2,-13}" & vbCrLf - s &= [String].Format(formatString, "Attribute", "Current Value", "Original Value") - s &= [String].Format(formatString, "---------", "-------------", "--------------") - i = 0 - cm = m_object.GetClassMap() - For i = 1 To cm.getKeySize - am = cm.getKeyAttributeMap(i) - x = m_object.GetValueByAttribute(am.Name) - Try - xstring = x.ToString - Catch ex As Exception - If x Is Nothing Then - xstring = "Nothing" - Else - xstring = "Unprintable" - End If - End Try - Try - If m_originalObject Is Nothing Then - ystring = "N/A" - Else - y = m_originalObject.GetValueByAttribute(am.Name) - ystring = y.ToString - End If - Catch ex As Exception - If y Is Nothing Then - ystring = "Nothing" - Else - ystring = "Unprintable" - End If - End Try - s &= [String].Format(formatString, am.Name, xstring, ystring) - Next - Return s - End Function + If m_object Is Nothing Then + Return "No persistent object" + End If + + s = m_object.GetObjectType.Name & ": " + indent = s.Length + If m_object.IsDirty Then + s &= "Dirty" + Else + s &= "Not Dirty" + End If + If m_object.Persistent Then + s &= ", Persistent" + Else + s &= ", NonPersistent" + End If + If m_object.IsLoading Then + s &= ", Loading" + Else + s &= ", Loaded" + End If + If m_object.AssociationsLoaded Then + s &= ", Assoc. Loaded" + Else + s &= ", No Assoc. Loaded" + End If + s &= vbCrLf + formatString = Space(indent) & "{0,-9}" & vbTab & "{1,-13}" & vbTab & "{2,-13}" & vbCrLf + s &= [String].Format(formatString, "Attribute", "Current Value", "Original Value") + s &= [String].Format(formatString, "---------", "-------------", "--------------") + i = 0 + cm = m_object.GetClassMap() + For i = 1 To cm.getKeySize + am = cm.getKeyAttributeMap(i) + x = m_object.GetValueByAttribute(am.Name) + Try + xstring = x.ToString + Catch ex As Exception + If x Is Nothing Then + xstring = "Nothing" + Else + xstring = "Unprintable" + End If + End Try + Try + If m_originalObject Is Nothing Then + ystring = "N/A" + Else + y = m_originalObject.GetValueByAttribute(am.Name) + ystring = y.ToString + End If + Catch ex As Exception + If y Is Nothing Then + ystring = "Nothing" + Else + ystring = "Unprintable" + End If + End Try + s &= [String].Format(formatString, am.Name, xstring, ystring) + Next + Return s + End Function End Class '''----------------------------------------------------------------------------- @@ -862,10 +882,12 @@ 'Lets cancel deletes first - just means resetting transaction to None If ce.TransactionType = CCacheEntry.CacheTransaction.Deleted AndAlso ce.PersistentObject.GetClassMap.RelationalDatabase Is reldb Then ce.TransactionType = CCacheEntry.CacheTransaction.None + ce.RestoreCollections() End If 'Now look for cache entries to be saved and restore them If ce.TransactionType = CCacheEntry.CacheTransaction.Saved AndAlso ce.PersistentObject.GetClassMap.RelationalDatabase Is reldb Then ce.RestoreObject() + ce.RestoreCollections() ce.TransactionType = CCacheEntry.CacheTransaction.None End If Next @@ -882,6 +904,19 @@ Next End Sub + Friend Sub StartTransaction(ByVal reldb As _CRelationalDatabase) + Dim x As DictionaryEntry + Dim ce As CCacheEntry + + 'precopy collection objects + For Each x In Me + ce = x.Value + If ce.PersistentObject.GetClassMap.RelationalDatabase Is reldb Then + ce.CopyCollections() + End If + Next + End Sub + Public Property ObjectsAreLoading() As Boolean Get Return m_objectsLoading @@ -941,7 +976,7 @@ pObj = ce.PersistentObject ck = x.Key outString &= i.ToString & ">" & ce.ToString & vbCrLf & _ - i.ToString & " (Cache Key)>" & ck.ToString & vbCrLf + i.ToString & " (Cache Key)>" & ck.ToString & vbCrLf i += 1 Next x outString &= ">>>> END CACHE DUMP <<<<" |