Filtered Save by Xal and other questions.

Filtered Save by Xal and other questions.

Old forum URL: forums.lhotka.net/forums/t/5445.aspx


wjcomeaux posted on Monday, September 22, 2008

Xal:

Back on this post http://forums.lhotka.net/forums/thread/1767.aspx, you mentioned some filtered save functionality. The link you reference is now dead. Is this something that you still consider as a workable idea or have you abandoned it for better things?

I'm curious as I currently have a realy large object graph that needs to be saved periodically and ideally would like to get away from the need to iterate across every single object to test "IsDirty".

Forum:
A few other questions:

I would like to investigate the ability for a parent to perform "sectioned" save operations. So possibly a Save function on my csla objects that take a set of boolean parameters to indicate which children to consider for this iteration. Something like a Save(saveTaxNoticeList, saveEquipmentList, saveBlah).
Is this something worth considering? Any caveats with CSLA?

The reason being is this is a "live" system. There is a part that's running on a "server" and then there are multiple types of clients interacting with the server via messages sent over TCP/IP. Clients can make adjustments to the live data as necessary and the server will persist the data in pieces since persistence of the entire system will cause performance hitches that we want to overcome. Essentially this entire database will exist in memory on one or more "servers". However, it still makes sense that everything is in the same object graph IE
EmployerList->Employer->TaxNoticeList->TaxNotice->TaxNoticeActionList->TaxNoticeAction
EmployerList->Employer->EquipmentList->Equipment->ServiceList->Service

We are also considering the ability to have the Clients interact directly with the database as well as send the new information to the server. Something like when a client hits "Save" the object they are working on is put directly in the database and then a message is sent to the server causing it to either fetch the object again from the db or (more likely) just to consume the newly changed information from the client.

The reason for this is there are multiple types of clients. ClientTypeX might be allowed to change an Employer's Name and ClientTypeY might be allowed to change Credit info. And so if X writes to the database we might lose changes from Y so it makes sense that all changes hit the server (if one is available) otherwise hit the database.

This gets more crazy as we go along but it's fun. :O

Thoughts?
Thanks,
Will

JoeFallon1 replied on Tuesday, September 23, 2008

For some reason the link is not dead for me.

Here is the code that Andres wrote almost 3 years ago. It was his 3rd stab at it but it looks like he never got much feedback from the final post. I merged his final 2 posts so you get his final code.

====================================================================

Well after some redoing of the things I posted before i have a version that seems to be working fine. There is some overhead introduced, but it's a huge gain on remote portals. If the dataportal runs locally, then it's pointless to use this.

I'm sure there are some optimizations that can be made. I hope that I can get your feedback on this!!

Here it is:


BusinessBase: Just add the following code. Nothing needs to be modified. To test this functionality just do:
yourBO = yourBO.SaveFiltered()

Ok, I improved the businessbase part a bit.

Here's the latest businessbase code:

Private mInternalID As Guid = Guid.NewGuid

Protected Friend ReadOnly Property InternalID() As Guid
Get
Return mInternalID
End Get
End Property

Protected Friend Function GetChanges() As BusinessBase
Dim clone As BusinessBase = DirectCast(Me.Clone, BusinessBase)
Dim _type As Type
_type = clone.GetType
For Each prop As PropertyInfo In _type.GetProperties()
If prop.PropertyType.IsSubclassOf(GetType(BusinessCollectionBase)) Then
Dim coll As BusinessCollectionBase
coll = DirectCast(prop.GetValue(clone, Nothing), BusinessCollectionBase)
coll.FilterClean()
End If
Next
Return clone
End Function

Protected Sub Merge(ByVal Unsaved As BusinessBase)
Dim _type As Type
_type = Me.GetType
For Each prop As PropertyInfo In _type.GetProperties()
If prop.PropertyType.IsSubclassOf(GetType(BusinessCollectionBase)) Then
Dim coll As BusinessCollectionBase
coll = DirectCast(prop.GetValue(Me, Nothing), BusinessCollectionBase)
coll.Merge(DirectCast(prop.GetValue(Unsaved, Nothing), BusinessCollectionBase))
End If
Next
End Sub

Public Overridable Function SaveFiltered() As BusinessBase
If Me.IsChild Then
Throw New NotSupportedException(GetResourceString("NoSaveChildException"))
End If

If EditLevel > 0 Then
Throw New ApplicationException(GetResourceString("NoSaveEditingException"))
End If

If Not IsValid Then
Throw New ValidationException(GetResourceString("NoSaveInvalidException"))
End If

If IsDirty Then
Dim FilteredUpdated As BusinessBase
FilteredUpdated = DirectCast(DataPortal.Update(Me.GetChanges), BusinessBase)
FilteredUpdated.Merge(Me)
Return FilteredUpdated
Else
Return Me
End If

End Function

------------------------------------------------------------------------------

BusinessCollectionBase: Modify the OnRemove Method to look like this:

Protected Overrides Sub OnRemove(ByVal index As Integer, ByVal value As Object)
If Not ActivelySorting Then
' when an object is 'removed' it is really
' being deleted, so do the deletion work
If Not Me.mSaveFiltering And Not Me.mSaveMerging Then
DeleteChild(CType(value, BusinessBase))
End If
MyBase.OnRemove(index, value)
End If
End Sub

And also add this code to BusinessCollectionBase:

Private mSaveFiltering As Boolean = False
Private mSaveMerging as Boolean = False

Protected Friend Sub FilterClean()
mSaveFiltering = True
For i As Integer = List.Count - 1 To 0 Step -1
If Not DirectCast(list.Item(i), BusinessBase).IsDirty Then
list.RemoveAt(i)
End If
Next
mSaveFiltering = False
End Sub

Protected Friend Sub Merge(ByVal Unsaved As BusinessCollectionBase)
Me.mSaveMerging = True
Dim x As Hashtable
x = New Hashtable(Me.List.Count)
For Each item As BusinessBase In list
x.Add(item.InternalID, item)
Next
''This is done so that the original order is preserved
list.Clear()
For Each item As BusinessBase In Unsaved
If Not item.IsDirty Then
list.Add(item)
Else
list.Add(x.Item(item.InternalID))
End If
Next
Me.mSaveMerging = False
End Sub

Andrés

====================================================================

He mentioned that if you were running locally then there would be no point in calling this.

Joe

 

wjcomeaux replied on Tuesday, September 23, 2008

Thanks Joe. Not sure why the link doesn't work for me. It takes me to a dead microsoft page..

Well, it doesn't look like I would gain a whole lot by using that system.

I'm leaning more towards a partitioned save method for overly large BOs.

Thanks,

Will

Copyright (c) Marimer LLC