Concurrency: Setting timestamp/userid for all entities in hierarchy

Posts   
 
    
Jean-Paul
User
Posts: 19
Joined: 22-Sep-2006
# Posted on: 09-Feb-2009 16:24:27   

Hi Guys

I'm using:

VS2005 LLBLGen with Adapter Classes for remoting Optimistic concurrency by means of Dependency Injection and ConcurrencyPredicateFactories.

My core tables implement the following fields for concurrency purposes.

Created DATETIME CreatedById INT Modified DATETIME ModifiedById INT

Given the following object hierarchy, each object/table implementing the abovementioned fields: Customer -> Invoice -> InvoiceLine

Ideally I would like to be able to update the Created(ById) and Modified(ById) fields for all New/Dirty entities down the object hierarchy by calling something like the following:


customer.SetUserId(1); // set CreatedById and ModifiedById on the client
customer.SetTimeStamp(DateTime.Now); // set Created and Modified TimeStamp on the remoting server.

Other than looping through each entity and related EntityCollection to set the values,** are there any existing best practices you can recommend for achieving the abovementioned?**

I suppose one could use the concurrency predicate factory for updating the timestamp fields when doing the concurrency checks. However, ideally I would like to specifiy the same date and time for all entities forming part of the same update.

I've also considered using dependency injection for injecting something like a static ConcurrencyManager class that would enable one to update all subscribed entities.


customer.ConcurrencyManager.SetUserId(1);
customer.ConcurrencyManager.SetTimeStamp(DateTime.Now);

// or

ConcurrencyManager.Instance.SetUserId(1); // using Singleton
ConcurrencyManager.Instance.SetTimeStamp(DateTime.Now); // using Singleton

However, I imagine the ConcurrencyManager would then have to be a public property on each of the entities for the injection to work. I must admit, I'm rather new to the whole subject of Dependency Injection and the way that it's implemented in LLBLgen, meaning i'm not altogether sure that this is even possible.

I really look forward to any suggestions.

Thank you

Jean-Paul

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 10-Feb-2009 03:12:48   

LLBLGen version? Using SelfServicing or Adapter?

David Elizondo | LLBLGen Support Team
Jean-Paul
User
Posts: 19
Joined: 22-Sep-2006
# Posted on: 10-Feb-2009 07:05:09   

Hi David

I'm using LLBLGen 2.6 Final released 6 October 2008 with Adapter classes. As mentioned in my original post I'm making use of Remoting.

Thank you.

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 10-Feb-2009 08:37:47   

You have many options here: 1- You can use a validator class to implement the ValidateEntityBeforeSave. And you may have one validator class that's used to validate all the entities you need.

2- Override the DataAccessAdapterBase SaveEntity & SaveEntityCollection methods. You can do this either on a partial class of the DataAccessAdapter. Or you can inherit from the DataAccessAdapter.

3- You can handle this on the entity level, by overriding the OnBeforeEntitySave, Or by overriding the OnValidateEntityBeforeSave.

rdhatch
User
Posts: 198
Joined: 03-Nov-2007
# Posted on: 10-Feb-2009 09:52:08   

Hi Jean-Paul,

Here is how I accomplished this very thing using Self-Servicing, although I'm not using DI here. To make sure every Entity has a Timestamp field, I created an interface called IOptimisticConcurrencyTimestamp - which, in my EntityConcurrencyFactory, allows me to cast all my entities to this interface and easily access the Timestamp field.

    Partial Public Class CommonEntityBase
        Implements IOptimisticConcurrencyTimestamp

        Protected Overrides Sub OnInitializing()
            MyBase.OnInitializing()

            'Set Concurrency Factory on all Entities (same shared instance)
            Me.ConcurrencyPredicateFactoryToUse = LLBLGenCommon.EntityConcurrencyFactory

            'Set Authorizer on all Entities (same shared instance)
            Me.AuthorizerToUse = LLBLGenCommon.EntityAuthorizer
        End Sub

#Region "Optimistic Concurrency"
        'This method Forces all tables to have a Timestamp field
        Public ReadOnly Property LastModifiedDateTime() As EntityField Implements IOptimisticConcurrencyTimestamp.LastModifiedTimestamp
            Get
                Dim ITimestampField As IEntityField = Me.Fields("LastModifiedDateTime")
                Dim TimestampField As EntityField = DirectCast(ITimestampField, EntityField)

                'No Optimistic Concurrency Field?
                If TimestampField Is Nothing Then Throw New ORMConcurrencyException("Optimistic Concurrency Field (Timestamp) Not Defined!", Me)

                Return TimestampField
            End Get
        End Property

        Protected Overrides Sub OnSave()
            MyBase.OnSave()
            'Some Databases (ie, Access, SQLServer Compact Edition) don't support a true Timestamp field in the DB, so we need to update the Timestamp here
            'For those Databases, Set Timestamp before Saving
            'Me.LastModifiedDateTime.CurrentValue = System.DateTime.Now
        End Sub
#End Region

Hope this helps!

Ryan

PS. As Walaa noted, I believe to capture the OnSave (or equivalent event) in Adapter you use a partial class of DataAccessAdapterBase, instead of CommonEntityBase