StrataFrame Forum

Best Practice for LastModifiedUser and LastModifiedDate fields in a BO

http://forum.strataframe.net/Topic16179.aspx

By Edhy Rijo - 5/6/2008

Hi,

For some tables it is very useful to have a fields that will tell the user when was the record changed and by who. 

  • Is there anything in SF logic to auto update this kind of fields?
  • If these fields should be updated manually what would be the correct event to use to have these fields updated?

Thansk!

By Bill Cunnien - 5/6/2008

Hey Edhy,

There are probably several ways to do this.  My approach is to create four fields on each table that I want to track this kind of activity: createby, createdate, editdby, editdate.  I retrieve the UserPK from the SF users table for the current user, then in the BO I use two built-in methods:  SetDefaultValues() and BeforeSave().  In the SetDefaultValues() I set the create information.  In the BeforeSave() I set the edit information.  I never have to think about it after that.

Hope that gives you some direction,
Bill

By Edhy Rijo - 5/6/2008

Bill Cunnien (05/06/2008)
In the SetDefaultValues() I set the create information.  In the BeforeSave() I set the edit information.  I never have to think about it after that.
Bill

Hi Bill,

Thanks for the information it does make sense, only that having to do that manually for all BO may be a bit of work BigGrin I have a couple of VFP project which will be re-created in SF and I need to automate process like this as much as possible.

By Bill Cunnien - 5/6/2008

You could create a BaseBO from which any BO could inherit that will need this kind of tracking.  For me, I am running into variations in the column names, so I have to address the issue on a BO by BO basis anyway.  It is relatively easy to setup...just one simple item in the mental checklist for creating a BO.  I could see where a BaseBO would be beneficial in a large, consistently named environment.
By Edhy Rijo - 5/6/2008

Thanks again Bill, a base BO makes sense, will try that path.
By Trent L. Taylor - 5/6/2008

We actually take a similar approach within our medical application on the CreatedBy and CreatedAt fields.  As for the editing fields, we have started implementing some auditing features on certain tables so that you can determine what was changed by the user.  Just know which user and the time is not generally enough...the next question is always, "what did they change?"

There are actually a number of ways to go about this...you can use event hooks that you can manually implement on your base BO (i.e. Beforesave, etc.).  We also have some classes that are actually in the framework to audit a BO record...though I will not tell you about all of that until we have the viewer ready BigGrin

We are implementing auditing several different ways within our medical application because some things are more sensitive than others...but the more auditing that you add, the slower things will become.  So this is one of those things that has to be used with caution.

By Edhy Rijo - 5/6/2008

We also have some classes that are actually in the framework to audit a BO record...though I will not tell you about all of that until we have the viewer ready BigGrin

I did notice the AudiDataChanges and NeverAuditDataChanges properties in the BO but could not find any information about those in the help or the forums.  For now I don't need any viewer because these fields will be shown in a StatusBar for some forms only.  So if there is something in the framework now I could use for this specific situation then I would like to, if not then I will have to go with the BO base class approach.

I agree that Audit Trail is a complicated stuff and in my VFP projects I used it in just 2 or 3 projects and in some specific forms to avoid the slow down of the DBF tables with memo fields.

By Trent L. Taylor - 5/6/2008

SF auditing requires that you have RBS.  All of this is done through the security side so that you can turn on auditing for one user and not another, etc.

But if you set the AuditDataChanges property to True, and then the current user supports auditing (which you cannot override at the moment), and you have your app setup with security...AND you have the SFSAuditDataChanges, SFSAuditDataFields, and SFSAuditEvents deployed to the same database as your other RBS tables, then it should work.

You can also call the audit methods directly by using the Auditor class in security:

MicroFour.StrataFrame.Security.Auditor.InsertAuditEvent("1234", "User logged in.", New Byte() {})
MicroFour.StrataFrame.Security.Auditor.InsertAuditEvent("1234", "Business object state prior to save...", SerializedByteArrayOfBO)

You can store anything that you want in the Byte Array parm.  We will have a delegate that you set in the viewer that gets called to determine how to show or view that data.  This approach allows you to store literally anything you want as additional data within the audit record.

By Greg McGuffey - 6/3/2008

I've been messing around with this and have run into a problem, born from pure ignorance. Unsure



How do you get a serializable object into a Byte array()? It's probably simple, but I'm not getting it.



Thanks!
By Trent L. Taylor - 6/3/2008

Since it is a byte array, you can put anything you want into it.  You can read a file from disk into a byte array and dump it in there, or whatever you want.  It doesn't have to be serialized, it just has to be formatted as a byte array.  When the reader becomes available, we will provide a delegate that will allow you to attach any type of reader you like so you reverse what you did going in and show it to the end-user however you would like.  But if you want to serialize something and convert it into a byte array, here is a sample of how to do that:

Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim outStream As New System.IO.MemoryStream()
Dim bo As New MyTestBO()

'-- Serialize the object into a memory stream
formatter.Serialize(outStream, bo)

'-- Now convert the stream into an array
MicroFour.StrataFrame.Security.Auditor.InsertAuditEvent("MyKey", "My Serialized Object", outStream.ToArray())

'-- Clean Up
bo.Dispose()
outStream.Dispose()

By Greg McGuffey - 6/3/2008

Way too easy...



Now, how do I get byte array back into the object via deserialization? Or probably more to the point, how do I turn the array into a memory stream?
By Trent L. Taylor - 6/4/2008

Here is an example if you have a file saved to disk.  In the auditing sample, the byte array will be given back to you through the delegate, so you would skip the part of loading the byte array.

Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim inStream As New System.IO.MemoryStream()
Dim inBytes As Byte() = System.IO.File.ReadAllBytes("c:\MySerializedFil.bin")
Dim MyBO As MyBoType

'-- Move the byte array into a stream
inStream.Write(inBytes, 0, inBytes.Length)

'-- Deserialize the object
MyBO = CType(formatter.Deserialize(inStream), MyBOType)
By Greg McGuffey - 6/4/2008

Thanks! I'm not too familiar with the IO class and streams. I was having trouble getting the byte array into the stream. Again not too hard once you understand it! BigGrin
By Greg McGuffey - 6/4/2008

Thanks for the help I got it working. However I had to wrap the deserialize call in a try/catch as it always threw an serializationexceptions:

'-- Function to return the appropriate object from an array of bytes.

Private Function GetItem(data As Byte()) As TestSerializableItem

Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()

using inStream As New System.IO.MemoryStream()



'-- used a test class that had the Serializable attribute set

Dim myItem As TestSerializableItem



'-- Move the byte array into a stream

inStream.Write(data, 0, data.Length)



'-- Deserialize the object. This always

Try

myItem = CType(formatter.Deserialize(inStream), TestSerializableItem)

Catch ex As Exception

End Try

End Using



Return myItem

End Function