From: Richard B. <rb...@us...> - 2005-02-16 22:23:49
|
Update of /cvsroot/jcframework/dotnet In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2407 Modified Files: Tag: v2_0 AssemblyInfo.vb CCacheEntry.vb CInjectedObject.vb CPersistenceBroker.vb CPersistentCollection.vb CPersistentObject.vb readme.html Log Message: Fixed collections and object state being restored after a transaction aborts. Index: AssemblyInfo.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/AssemblyInfo.vb,v retrieving revision 1.14 retrieving revision 1.14.2.1 diff -u -d -r1.14 -r1.14.2.1 --- AssemblyInfo.vb 23 Dec 2004 01:14:44 -0000 1.14 +++ AssemblyInfo.vb 16 Feb 2005 22:23:38 -0000 1.14.2.1 @@ -25,7 +25,7 @@ ' by using the '*' as shown below -<Assembly: AssemblyVersion("2.1.0.0")> +<Assembly: AssemblyVersion("2.0.0.1")> ''' ----------------------------------------------------------------------------- ''' Project : AToMSFramework Index: CPersistenceBroker.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CPersistenceBroker.vb,v retrieving revision 1.89.2.2 retrieving revision 1.89.2.3 diff -u -d -r1.89.2.2 -r1.89.2.3 --- CPersistenceBroker.vb 10 Feb 2005 22:42:11 -0000 1.89.2.2 +++ CPersistenceBroker.vb 16 Feb 2005 22:23:38 -0000 1.89.2.3 @@ -201,7 +201,7 @@ If Not x Is Nothing Then Throw x End If - End Try + End Try End SyncLock End Function @@ -307,6 +307,9 @@ tmpObj = m_cache.Item(obj) End If End If + If tmpObj Is Nothing AndAlso m_cache.Status = CCacheCollection.StatusEnum.Deleted Then + Return False + End If If Not tmpObj Is Nothing Then t = tmpObj.GetObjectType If t Is obj.GetObjectType Or t.IsSubclassOf(obj.GetObjectType) Then @@ -751,12 +754,12 @@ End Try End If If found Then - Value.IsDirty = False 'After populating a new object - Value.OriginalCacheKey = New CCacheKey(Value) + 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 - 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)) @@ -776,11 +779,11 @@ If udaMap.LazyLoad Then cursor.loadProxy(anObjPers.GetSourceObject) Else - cursor.loadObject(anObjPers.GetSourceObject) - 'To prevent recursive associations causing problems we need to add the - 'object to the cache here as a preliminary measure - m_cache.Add(anObjPers) - retrieveAssociations(anObjPers, conn, anObjPers.GetClassMap, useCache) + cursor.loadObject(anObjPers.GetSourceObject) + 'To prevent recursive associations causing problems we need to add the + 'object to the cache here as a preliminary measure + m_cache.Add(anObjPers) + retrieveAssociations(anObjPers, conn, anObjPers.GetClassMap, useCache) End If anObjPers.IsDirty = False 'After populating a new object anObjPers.OriginalCacheKey = New CCacheKey(anObjPers) @@ -979,7 +982,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 @@ -994,7 +997,7 @@ If Not x Is Nothing Then Throw x End If - End Try + End Try End SyncLock End Sub @@ -1067,26 +1070,26 @@ udaMap = cm.AssociationMaps.Item(myKeys(i)) If udaMap.DeleteAutomatic Then For j = 1 To udaMap.getSize - If udaMap.Cardinality = CUDAMap.CardinalityEnum.ONE_TO_ONE Then + If udaMap.Cardinality = CUDAMap.CardinalityEnum.ONE_TO_ONE Then Value = obj.GetObjectByAttribute(udaMap.Target) - If Not Value Is Nothing Then - If retrieveObject(Value, False, True) Then - 'If Value.Retrieve() Then - deletePrivateObject(Value, conn, deleteSuperClass) - End If + If Not Value Is Nothing Then + If retrieveObject(Value, False, True) Then + 'If Value.Retrieve() Then + deletePrivateObject(Value, conn, deleteSuperClass) End If + End If ElseIf udaMap.Cardinality = CUDAMap.CardinalityEnum.ONE_TO_MANY Then col = obj.GetCollectionByAttribute(udaMap.Target) - If Not col Is Nothing Then - For k = 0 To col.Count - 1 - If col.Item(k).GetType.IsSubclassOf(GetType(CPersistentObject)) Then - Value = col.Item(k) - Else - Value = Me.getInjectedObject(col.Item(k)) - End If - If retrieveObject(Value, False, True) Then + If Not col Is Nothing Then + For k = 0 To col.Count - 1 + If col.Item(k).GetType.IsSubclassOf(GetType(CPersistentObject)) Then + Value = col.Item(k) + Else + Value = Me.getInjectedObject(col.Item(k)) + End If + If retrieveObject(Value, False, True) Then 'If Value.Retrieve Then - deletePrivateObject(Value, conn, deleteSuperClass) + deletePrivateObject(Value, conn, deleteSuperClass) End If Next k col = Nothing @@ -1098,11 +1101,11 @@ Dim statement As CSqlStatement statement = clMap.getDeleteSqlFor(obj) - Try - PCSQLHits.Increment() - Catch - End Try - conn.processStatement(statement) + Try + PCSQLHits.Increment() + Catch + End Try + conn.processStatement(statement) If deleteSuperClass Then cm = cm.SuperClass @@ -1112,9 +1115,9 @@ If retrieveObject(Value, False, True) Then 'If Value.Retrieve() Then deletePrivateObject(Value, conn, True) - End If - End If - End If + End If + End If + End If obj.Persistent = False If m_useCache Then @@ -1123,7 +1126,7 @@ PCCacheSize.RawValue = m_cache.Count Catch End Try - End If + End If colCriteriaParameters = Nothing End Sub @@ -1825,9 +1828,9 @@ Return cursor Catch - firstClassMap.RelationalDatabase.freeConnection(conn) - Return Nothing - End Try + firstClassMap.RelationalDatabase.freeConnection(conn) + Return Nothing + End Try End Function @@ -2508,8 +2511,8 @@ Next Next k End If - End If - End If + End If + End If Next i For i = 1 To cm.getInverseAssociationMapSize @@ -2612,19 +2615,19 @@ Return getObjectsToSave(obj, True, checkAssociationsRecursivly) End Function - Friend Sub GetLoginDetails(ByVal sender As _CRelationalDatabase, ByRef User As String, ByRef Password As String) + Public Sub GetLoginDetails(ByVal sender As _CRelationalDatabase, ByRef User As String, ByRef Password As String) RaiseEvent LoginDetailsNeeded(sender, User, Password) End Sub - Friend Sub startCacheTransaction(ByVal reldb As _CRelationalDatabase) + Public Sub startCacheTransaction(ByVal reldb As _CRelationalDatabase) m_cache.StartTransaction(reldb) End Sub - Friend Sub commitCache(ByVal reldb As _CRelationalDatabase) + Public Sub commitCache(ByVal reldb As _CRelationalDatabase) m_cache.CommitChanges(reldb) End Sub - Friend Sub rollbackCache(ByVal reldb As _CRelationalDatabase) + Public Sub rollbackCache(ByVal reldb As _CRelationalDatabase) m_cache.AbortChanges(reldb) End Sub @@ -2967,13 +2970,13 @@ PCAverageTimeBase.RawValue = 0 Catch ex As Exception Trace.WriteLine("Could not create performance counters. If using ASP.NET please see http://objectsharp.com/Blogs/bruce/archive/2003/12/05/222.aspx" & _ - vbCrLf & ex.Message) + vbCrLf & ex.Message) End Try End Sub Public Shared Sub CopyCollections(ByVal fromObject As IPersistableObject, ByRef toObject As IPersistableObject) Dim t, iEnumerableType, iListType, iDicType As Type - Dim coll, collItem As Object + Dim fromColl, toColl, collItem As Object Dim il As IList Dim id As IDictionary Dim f, fields() As FieldInfo @@ -2990,31 +2993,69 @@ 'Getting the ICloneable interface from the object. If Not f.GetValue(fromObject.GetSourceObject) Is Nothing Then Dim IClone As ICloneable = CType(f.GetValue(fromObject.GetSourceObject), ICloneable) - coll = IClone.Clone() + toColl = IClone.Clone() Else - coll = Nothing + toColl = Nothing End If Else - If Not f.GetValue(fromObject.GetSourceObject) Is Nothing Then + fromColl = f.GetValue(fromObject.GetSourceObject) + If Not fromColl Is Nothing Then 'If the field doesn't support the ICloneable interface then just set it. - coll = Activator.CreateInstance(f.FieldType) + t = f.FieldType + toColl = Activator.CreateInstance(t) 'need to copy references one-by-one + 'Also neeed to connect event handlers of new collection to new object based + 'on event connections of the old collection and the old object + Dim fColl, fieldsColl() As FieldInfo + Dim d, newD, delArray(), newDelArray() As [Delegate] + Dim collDel As MulticastDelegate + While Not t Is Nothing + fieldsColl = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) + For Each fColl In fieldsColl + If fColl.FieldType.Name = fColl.Name & "Handler" OrElse fColl.FieldType Is GetType(EventHandler) Then + 'This is an event! + d = fColl.GetValue(fromColl) + collDel = Nothing + If Not d Is Nothing Then + delArray = d.GetInvocationList + For Each d In delArray + If Not d.Target.GetType.GetInterface("IPersistableObject") Is Nothing Then + If CType(d.Target, IPersistableObject).Equals(fromObject.GetSourceObject) Then + newD = MulticastDelegate.CreateDelegate(fColl.FieldType, toObject, d.Method.Name) + If collDel Is Nothing Then + collDel = newD + Else + collDel.Combine(collDel, newD) + End If + End If + End If + Next + fColl.SetValue(toColl, collDel) + End If + End If + Next + If t.IsSubclassOf(GetType(CPersistentCollection)) Then + t = t.BaseType + Else + t = Nothing + End If + End While If Not iListType Is Nothing Then - il = CType(coll, IList) - For Each collItem In f.GetValue(fromObject.GetSourceObject) + il = CType(toColl, IList) + For Each collItem In fromColl il.Add(collItem) Next Else - id = CType(coll, IDictionary) - For Each de As DictionaryEntry In f.GetValue(fromObject.GetSourceObject) + id = CType(toColl, IDictionary) + For Each de As DictionaryEntry In fromColl id.Add(de.Key, de.Value) Next End If Else - coll = Nothing + toColl = Nothing End If End If - f.SetValue(toObject, coll) + f.SetValue(toObject.GetSourceObject, toColl) End If Next End Sub Index: readme.html =================================================================== RCS file: /cvsroot/jcframework/dotnet/readme.html,v retrieving revision 1.7 retrieving revision 1.7.2.1 diff -u -d -r1.7 -r1.7.2.1 --- readme.html 2 Feb 2005 05:08:38 -0000 1.7 +++ readme.html 16 Feb 2005 22:23:39 -0000 1.7.2.1 @@ -117,7 +117,7 @@ <!-- BEGIN faq_block --> <table class="forumline" width="100%" cellspacing="1" cellpadding="2" border="0" align="center"> <tr> - <td class="catHead" height="28" align="center"><span class="cattitle">Atoms Framework 2.1<br />Released: xx-xxx-200x</span></td> + <td class="catHead" height="28" align="center"><span class="cattitle">Atoms Framework 2.0.1<br />Released: 17-Feb-2005</span></td> </tr> <tr> <td align="center"> @@ -149,10 +149,10 @@ </div> <tr> <td class="groupHeader" align="left" valign="top"> - <div onclick="return CFAQ.display('faq_a_notesv2.1', false);" style="width:100%;cursor:pointer;cursor:hand;"> - <span class="gen"><a class="postlink" href="javascript:void(0)" onclick="return CFAQ.display('faq_a_notesv2.1', true);" onfocus="this.blur();"><b>Changes in v2.1 (xx-xxx-xx)</b></a></span> + <div onclick="return CFAQ.display('faq_a_notesv2.0.1', false);" style="width:100%;cursor:pointer;cursor:hand;"> + <span class="gen"><a class="postlink" href="javascript:void(0)" onclick="return CFAQ.display('faq_a_notesv2.0.1', true);" onfocus="this.blur();"><b>Changes in v2.0.1 (17-Feb-05)</b></a></span> </div> - <div id="faq_a_notesv2.1" style="display:none;"> + <div id="faq_a_notesv2.0.1" style="display:none;"> <table class="details" width="100%" cellspacing="0" cellpadding="3" border="0" align="left"> <tr class="tableCaption"><td colspan=3>Bugs:</td></tr> <tr class="tableHeader" valign="bottom"><td valign="top" class="colHeader">Job Id</td> @@ -181,8 +181,8 @@ </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> + <td class="col1">Objects/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. This also includes objects that were modified but not saved during the transaction<td> </tr> <tr class="tableCaption" valign="bottom"> <td colspan=3><br/>Modifications</td> Index: CCacheEntry.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CCacheEntry.vb,v retrieving revision 1.25.2.3 retrieving revision 1.25.2.4 diff -u -d -r1.25.2.3 -r1.25.2.4 --- CCacheEntry.vb 10 Feb 2005 22:42:11 -0000 1.25.2.3 +++ CCacheEntry.vb 16 Feb 2005 22:23:38 -0000 1.25.2.4 @@ -30,6 +30,7 @@ Private m_object As IPersistableObject Private m_objectCopy As IPersistableObject + Private m_objectCopyAtTransactionStart As IPersistableObject Private m_transactionType As CCacheEntry.CacheTransaction Private m_expiryTime As Date Private m_originalObject As IPersistableObject @@ -115,84 +116,77 @@ End Property Public Sub CopyObject() + 'Need two object copies + 'One for the copy made at the very start of the transaction + 'One for any updates made to the object (via save) during the transaction so that + ' cache reads get the correct copy m_objectCopy = m_object.Copy + m_objectCopyAtTransactionStart = m_object.Copy End Sub - Public Sub RestoreObject() - m_object = m_objectCopy - End Sub - - Public Sub CopyCollections() - 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 - - '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 + Public Sub UpdateObject() + m_objectCopy = m_object.Copy + End Sub - 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) - 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. - If Not f.GetValue(m_object.GetSourceObject) Is Nothing Then - Dim IClone As ICloneable = CType(f.GetValue(m_object.GetSourceObject), ICloneable) - coll = IClone.Clone() - Else - coll = Nothing - End If - Else - If Not f.GetValue(m_object.GetSourceObject) Is Nothing Then - '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.GetSourceObject) - il.Add(obj) - Next - Else - id = CType(coll, IDictionary) - For Each de As DictionaryEntry In f.GetValue(m_object.GetSourceObject) - id.Add(de.Key, de.Value) - Next - End If - Else - coll = Nothing - End If - End If - m_collectionCollection.Add(f.Name, coll) - End If - Next + Public Sub RestoreObject() + m_object = m_objectCopyAtTransactionStart End Sub - Public Sub RestoreCollections() - Dim t, iEnumerableType, iListType, iDicType As Type - Dim f, fields() As FieldInfo + ' Public Sub CopyCollections() + ' 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 - 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) - 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.GetSourceObject, m_collectionCollection.Item(f.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) + ' 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. + ' If Not f.GetValue(m_object.GetSourceObject) Is Nothing Then + ' Dim IClone As ICloneable = CType(f.GetValue(m_object.GetSourceObject), ICloneable) + ' coll = IClone.Clone() + ' Else + ' coll = Nothing + ' End If + ' Else + ' If Not f.GetValue(m_object.GetSourceObject) Is Nothing Then + ' '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.GetSourceObject) + ' il.Add(obj) + ' Next + ' Else + ' id = CType(coll, IDictionary) + ' For Each de As DictionaryEntry In f.GetValue(m_object.GetSourceObject) + ' id.Add(de.Key, de.Value) + ' Next + ' End If + ' Else + ' coll = Nothing + ' End If + ' End If + ' m_collectionCollection.Add(f.Name, coll) ' End If ' Next + '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) + ' 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.GetSourceObject, m_collectionCollection.Item(f.Name)) ' End If ' Next + 'End Sub Public Sub New(ByVal lifetime As Double) m_expiryTime = DateAdd(DateInterval.Minute, lifetime, Now) @@ -587,6 +581,13 @@ Private m_expiryInterval As Double Private m_objectsLoading As Boolean + Public Enum StatusEnum + Found + NotFound + Deleted + End Enum + Private m_status As StatusEnum + '''----------------------------------------------------------------------------- ''' <summary> ''' Adds a persistent object to the cache. @@ -630,7 +631,6 @@ End If If obj.GetClassMap.RelationalDatabase.getConnection(Nothing).Started Then ce.TransactionType = CCacheEntry.CacheTransaction.Saved - ce.CopyObject() End If Try ce.OriginalObject = obj @@ -713,15 +713,18 @@ If found Then 'Debug.WriteLine("Cache - Found object in cache") If ce.TransactionType = CCacheEntry.CacheTransaction.Deleted Then + m_status = StatusEnum.Deleted Return Nothing End If Debug.WriteLine([String].Format("Cache - getting {0} object from cache. Key..." & vbCrLf & x.Key.ToString, x.Key.ObjType.Name)) + m_status = StatusEnum.Found Return GetCachedObject(ce) End If End If End If End While 'Debug.WriteLine("Cache - object not cached") + m_status = StatusEnum.NotFound Return Nothing End Function @@ -743,16 +746,22 @@ Get Dim ce As CCacheEntry ce = MyBase.Item(OIDValue) - If ce Is Nothing Then Return Nothing + If ce Is Nothing Then + m_status = StatusEnum.NotFound + Return Nothing + End If If ce.IsExpired() And Not ce.PersistentObject.IsDirty Then 'Debug.WriteLine("Cache - removing expired object: " & OIDValue) - MyBase.Remove(Item) 'delete object from cache + MyBase.Remove(Item) 'delete object from cache + m_status = StatusEnum.NotFound Return Nothing End If 'Debug.WriteLine("Cache - returning copy of object from cache" & OIDValue) If ce.TransactionType = CCacheEntry.CacheTransaction.Deleted Then + m_status = StatusEnum.Deleted Return Nothing End If + m_status = StatusEnum.Found Return GetCachedObject(ce) End Get End Property @@ -778,19 +787,26 @@ Dim ckey As CCacheKey ckey = New CCacheKey(obj) If Not ckey.hasLegitValues Then + m_status = StatusEnum.NotFound Return Nothing End If ce = MyBase.Item(ckey) - If ce Is Nothing Then Return Nothing + If ce Is Nothing Then + m_status = StatusEnum.NotFound + Return Nothing + End If If ce.IsExpired() And Not ce.PersistentObject.IsDirty Then 'Debug.WriteLine([String].Format("Cache - removing expired {0} object. Key... " & vbCrLf & ckey.ToString, ckey.ObjType.Name)) - MyBase.Remove(ckey) 'delete object from cache + MyBase.Remove(ckey) 'delete object from cache + m_status = StatusEnum.NotFound Return Nothing End If If ce.TransactionType = CCacheEntry.CacheTransaction.Deleted Then + m_status = StatusEnum.Deleted Return Nothing End If Debug.WriteLine([String].Format("Cache - getting {0} object from cache. Key..." & vbCrLf & ckey.ToString, ckey.ObjType.Name)) + m_status = StatusEnum.Found Return GetCachedObject(ce) End Get End Property @@ -900,16 +916,23 @@ For Each x In Me ce = x.Value - '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 + If ce.PersistentObject.GetClassMap.RelationalDatabase Is reldb Then + Select Case ce.TransactionType + Case CCacheEntry.CacheTransaction.None + 'Even though an object hasn't been saved/deleted it may still have been + 'modified so we must restore it in any case + ce.RestoreObject() + Case CCacheEntry.CacheTransaction.Deleted + 'Lets cancel deletes first - just means resetting transaction to None + ce.TransactionType = CCacheEntry.CacheTransaction.None + 'ce.RestoreCollections() + ce.RestoreObject() + Case CCacheEntry.CacheTransaction.Saved + 'Now look for cache entries to be saved and restore them + ce.RestoreObject() + 'ce.RestoreCollections() + ce.TransactionType = CCacheEntry.CacheTransaction.None + End Select End If Next @@ -933,7 +956,8 @@ For Each x In Me ce = x.Value If ce.PersistentObject.GetClassMap.RelationalDatabase Is reldb Then - ce.CopyCollections() + 'ce.CopyCollections() + ce.CopyObject() End If Next End Sub @@ -1004,4 +1028,10 @@ Return outString End Function + + Public ReadOnly Property Status() As StatusEnum + Get + Return m_status + End Get + End Property End Class \ No newline at end of file Index: CInjectedObject.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CInjectedObject.vb,v retrieving revision 1.9.2.2 retrieving revision 1.9.2.3 diff -u -d -r1.9.2.2 -r1.9.2.3 --- CInjectedObject.vb 10 Feb 2005 22:42:11 -0000 1.9.2.2 +++ CInjectedObject.vb 16 Feb 2005 22:23:38 -0000 1.9.2.3 @@ -76,15 +76,20 @@ End If Dim f, fields() As FieldInfo Dim value As Object - Dim t As Type + Dim t, iListType, iDicType As Type Try t = sourceObject.GetType While Not t Is Nothing fields = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) 'Note that this will copy event handlers as well For Each f In fields - value = f.GetValue(sourceObject) - f.SetValue(targetObject, value) + iListType = f.FieldType.GetInterface("IList", True) + iDicType = f.FieldType.GetInterface("IDictionary", True) + 'Do not copy collections yet - we'll do that at the end + If iListType Is Nothing AndAlso iDicType Is Nothing Then + value = f.GetValue(sourceObject) + f.SetValue(targetObject, value) + End If Next If Not t.BaseType Is Nothing Then t = t.BaseType @@ -92,6 +97,7 @@ t = Nothing End If End While + CPersistenceBroker.CopyCollections(sourceObject, targetObject) Catch ex As Exception Debug.WriteLine(ex.Message) End Try Index: CPersistentCollection.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CPersistentCollection.vb,v retrieving revision 1.15 retrieving revision 1.15.2.1 diff -u -d -r1.15 -r1.15.2.1 --- CPersistentCollection.vb 21 Dec 2004 21:57:12 -0000 1.15 +++ CPersistentCollection.vb 16 Feb 2005 22:23:39 -0000 1.15.2.1 @@ -1,5 +1,7 @@ Option Explicit On Imports System.ComponentModel +Imports System.Reflection + '''----------------------------------------------------------------------------- ''' Project : AToMSFramework ''' Class : CPersistentCollection @@ -41,7 +43,8 @@ ''' [rbanks] 17/12/2003 Created ''' </history> '''----------------------------------------------------------------------------- - <Browsable(False)> Public Property ContainerObject() As CPersistentObject + <Browsable(False), Obsolete("Changes in collection management have made this obsolete. Please use the ListChanged event for equivalent functionality")> _ + Public Property ContainerObject() As CPersistentObject Get Return m_container End Get @@ -195,10 +198,10 @@ Private Sub ItemDirtiedHandler(ByVal sender As Object, ByVal e As EventArgs) ' Debug.WriteLine("Collection trapped item dirtied event for " & sender.GetType.Name) RaiseEvent ItemDirtied(Me, e) - If Not Me.ContainerObject Is Nothing Then - 'Debug.WriteLine("item dirtied - dirtying container") - ContainerObject.SetDirtyFlag() - End If + 'If Not Me.ContainerObject Is Nothing Then + ' 'Debug.WriteLine("item dirtied - dirtying container") + ' ContainerObject.SetDirtyFlag() + 'End If End Sub #End Region @@ -342,4 +345,49 @@ RaiseEvent Disposed(Me, EventArgs.Empty) End Sub #End Region + + '#Region "ICloneable Interface" + ' Public Function Clone() As Object Implements System.ICloneable.Clone + ' Dim obj As CPersistentCollection + ' 'obj = Me.MemberwiseClone + ' 'Use reflection to copy all of the fields from Obj to me (by value) + ' Dim f, fields() As FieldInfo + ' Dim e, events() As EventInfo + ' Dim value As Object + ' Dim t As Type + ' Dim d, newD, delArray() As [Delegate] + ' Try + ' t = Me.GetType + ' obj = Activator.CreateInstance(t) + ' While Not t Is Nothing + ' fields = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) + ' 'Note that this will copy event handlers as well + ' For Each f In fields + ' If f.FieldType.Name = f.Name & "Handler" Then + ' 'This is an event! + ' d = f.GetValue(Me) + ' If Not d Is Nothing Then + ' delArray = d.GetInvocationList + ' For Each d In delArray + ' If d.Target.Equals(obj) Then + ' newD = [Delegate].CreateDelegate(f.FieldType, obj, d.Target) + ' End If + ' Next + ' End If + ' End If + ' value = f.GetValue(Me) + ' f.SetValue(obj, value) + ' Next + ' If t.IsSubclassOf(GetType(CPersistentCollection)) Then + ' t = t.BaseType + ' Else + ' t = Nothing + ' End If + ' End While + ' Catch ex As Exception + ' Debug.WriteLine(ex.Message) + ' End Try + ' Return obj + ' End Function + '#End Region End Class Index: CPersistentObject.vb =================================================================== RCS file: /cvsroot/jcframework/dotnet/CPersistentObject.vb,v retrieving revision 1.52.2.1 retrieving revision 1.52.2.2 diff -u -d -r1.52.2.1 -r1.52.2.2 --- CPersistentObject.vb 10 Feb 2005 22:42:13 -0000 1.52.2.1 +++ CPersistentObject.vb 16 Feb 2005 22:23:39 -0000 1.52.2.2 @@ -675,7 +675,7 @@ Return CallByName(o, propertyName, CallType.Get) End If Catch err As Exception - Throw New Exception("getCollectionByAttribute failed", err) + Throw New Exception("getCollectionByAttribute failed for class " & me.getclassmap.name & " attribute: " & " " & pname, err) End Try End Function @@ -901,16 +901,16 @@ Do While queue.Count > 0 value = queue.Dequeue() Try - ckey = New CCacheKey(value) - If savedKeys.Contains(ckey) Then - 'object was already saved (could be new object referenced by multiple other new objects) - Debug.WriteLine("The object with key " & ckey.ToString & " was already saved once") - Else - persistentBroker.saveObject(value) - 'Need to recalculate the key here to handle objects using identity (autonumber) keys ckey = New CCacheKey(value) - savedKeys.Add(ckey) - End If + If savedKeys.Contains(ckey) Then + 'object was already saved (could be new object referenced by multiple other new objects) + Debug.WriteLine("The object with key " & ckey.ToString & " was already saved once") + Else + persistentBroker.saveObject(value) + 'Need to recalculate the key here to handle objects using identity (autonumber) keys + ckey = New CCacheKey(value) + savedKeys.Add(ckey) + End If Catch ex As Exception 'After an error remove the cached object so that the next retrieve ' will refresh the cache with the item from the database @@ -1112,12 +1112,18 @@ ''' </history> '''----------------------------------------------------------------------------- Public Sub SetDirtyFlag() + Static eventRaised As Boolean If m_persistent = True Then m_modifiedDate = Now 'Only set modified if the object is already persistent 'Set milliseconds to zero to avoid issues with millisecond inconsistencies in SQL db's m_modifiedDate = m_modifiedDate.AddMilliseconds(-m_modifiedDate.Millisecond) If m_dirty = False Then - RaiseEvent MarkedAsDirty(Me, New EventArgs) + If Not eventRaised Then + 'Was getting stack overflow with direct many-to-many associations due to circular event raising + eventRaised = True + RaiseEvent MarkedAsDirty(Me, New EventArgs) + eventRaised = False + End If End If End If m_dirty = True @@ -1225,10 +1231,14 @@ '''----------------------------------------------------------------------------- Public Overridable Function Copy() As IPersistableObject Implements IPersistentObject.Copy Dim obj As CPersistentObject - obj = CType(Me.MemberwiseClone, CPersistentObject) + obj = Me.getClassMap.CreateObjectInstance + obj.ReplaceWith(Me) + 'obj = CType(Me.MemberwiseClone, CPersistentObject) 'Because memberwise clone only reference copies collections we should also 'copy the collections as well, since failing to do so can corrupt the cache. - CPersistenceBroker.CopyCollections(Me, obj) + 'This will also connect events from the collections to the new object based on + 'existing event handlers in "Me" + 'CPersistenceBroker.CopyCollections(Me, obj) Return obj End Function @@ -1246,22 +1256,27 @@ '''----------------------------------------------------------------------------- Public Overridable Sub ReplaceWith(ByVal obj As IPersistableObject) Implements IPersistentObject.ReplaceWith 'Use reflection to copy all of the fields from Obj to me (by value) + 'Also copy collections If obj Is Nothing Then Return If Not obj.GetObjectType Is Me.GetObjectType Then Throw New Exception("Objects must be of the same type") End If Dim f, fields() As FieldInfo Dim value As Object - Dim t As Type + Dim t, iListType, iDicType As Type Try t = Me.GetType While Not t Is Nothing fields = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) 'Note that this will copy event handlers as well For Each f In fields - ' Debug.WriteLine(f.Name) - value = f.GetValue(obj) - f.SetValue(Me, value) + iListType = f.FieldType.GetInterface("IList", True) + iDicType = f.FieldType.GetInterface("IDictionary", True) + 'Do not copy collections yet - we'll do that at the end + If iListType Is Nothing AndAlso iDicType Is Nothing Then + value = f.GetValue(obj) + f.SetValue(Me, value) + End If Next If t.IsSubclassOf(GetType(CPersistentObject)) Then t = t.BaseType @@ -1269,6 +1284,7 @@ t = Nothing End If End While + CPersistenceBroker.CopyCollections(obj, Me) Catch ex As Exception Debug.WriteLine(ex.Message) End Try @@ -1663,37 +1679,37 @@ Return obj End Function - <EditorBrowsable(EditorBrowsableState.Advanced)> _ - Public Overridable Function GetBaseCopy() As CPersistentObject - 'Use reflection to copy all of the fields from Obj to me (by value) - If Me.getClassMap.SuperClass Is Nothing Then - Return Nothing - End If - Dim obj As CPersistentObject = Me.getClassMap.SuperClass.CreateObjectInstance - Dim f, fields() As FieldInfo - Dim value As Object - Dim t As Type - Try - t = obj.GetType - While Not t Is Nothing - fields = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) - 'Note that this will copy event handlers as well - For Each f In fields - value = f.GetValue(Me) - f.SetValue(obj, value) - Next - If t.IsSubclassOf(GetType(CPersistentObject)) Then - t = t.BaseType - Else - t = Nothing - End If - End While - Return obj - Catch ex As Exception - Debug.WriteLine(ex.Message) - End Try - Return Nothing - End Function + '<EditorBrowsable(EditorBrowsableState.Advanced)> _ + 'Public Overridable Function GetBaseCopy() As CPersistentObject + ' 'Use reflection to copy all of the fields from Obj to me (by value) + ' If Me.getClassMap.SuperClass Is Nothing Then + ' Return Nothing + ' End If + ' Dim obj As CPersistentObject = Me.getClassMap.SuperClass.CreateObjectInstance + ' Dim f, fields() As FieldInfo + ' Dim value As Object + ' Dim t As Type + ' Try + ' t = obj.GetType + ' While Not t Is Nothing + ' fields = t.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public) + ' 'Note that this will copy event handlers as well + ' For Each f In fields + ' value = f.GetValue(Me) + ' f.SetValue(obj, value) + ' Next + ' If t.IsSubclassOf(GetType(CPersistentObject)) Then + ' t = t.BaseType + ' Else + ' t = Nothing + ' End If + ' End While + ' Return obj + ' Catch ex As Exception + ' Debug.WriteLine(ex.Message) + ' End Try + ' Return Nothing + 'End Function #End Region |