Concurrency issues

Posts   
 
    
Seanw
User
Posts: 9
Joined: 30-Nov-2005
# Posted on: 30-Nov-2005 07:28:57   

Hi everyone,

I am new user to LLBLGen and I think its a great product. I am trying to get simple concurrency working using a MS SQL Server timestamp column. I have read the many posts on this forum regarding concurrency but still have problems getting it to work.

The code is as follows, In the Entity class I have included this private class,

Private Class CustomerConcurrencyPredicateFactory
            Implements IConcurrencyPredicateFactory

            Public Function CreatePredicate( _
                                            ByVal predicateTypeToCreate As ConcurrencyPredicateType, ByVal containingEntity As Object) _
            As IPredicateExpression Implements IConcurrencyPredicateFactory.CreatePredicate

                Dim toReturn As IPredicateExpression = New PredicateExpression
                Dim customer As Test1Entity = CType(containingEntity, Test1Entity)

                '''''''''''''''''''''''''
                Select Case predicateTypeToCreate

                    Case ConcurrencyPredicateType.Delete

                    Case ConcurrencyPredicateType.Save
                        ' only for updates
                        toReturn.Add(PredicateFactory.CompareValue( _
                                                            Test1FieldIndex.Ts, _
                                                            ComparisonOperator.Equal, _
                                                           customer.Fields(CType(Test1FieldIndex.TimeStampColumn, Integer)).CurrentValue))
                End Select

                Return toReturn

            End Function
        End Class

Also in the Entity class I have added this method to call the CreatePredicate method, BTW I was planning in putting this call in a more automated place once I have it working, e.g. during the save function.



 Public Sub AddPredicate()

            Dim CPF As New CustomerConcurrencyPredicateFactory
            CPF.CreatePredicate(ConcurrencyPredicateType.Save, Me)
            Me.ConcurrencyPredicateFactoryToUse = CPF

        End Sub


From the PL I create an instance of the entity for one record

 Dim newValues As New DAL.EntityClasses.Test1Entity("1")

Change the record in Enterprise manager, then update the Test1Entity column

 newValues.Col1 = Me.TextBox1.Text

Save the updated entity

  Dim boolSavedRecord As Boolean = adapter.SaveEntity(newValues)

        If boolSavedRecord Then
            MessageBox.Show("Saved")
        Else
            MessageBox.Show("Not Saved")
        End If

However, even though the updated newValues record does not get written to the table, SaveEntity always returns true. I dont get any Exception at all.

Thanks,

Sean

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 30-Nov-2005 09:45:26   

Could you set a breakpoint in CreatePredicate and see if it is hit? Also, could you enable DQE tracing (see 'Troubleshooting and debugging' in the documentation) to see what the query is that's been generated so you can check if the query contains the timestamp restriction?

Frans Bouma | Lead developer LLBLGen Pro
Seanw
User
Posts: 9
Joined: 30-Nov-2005
# Posted on: 30-Nov-2005 10:58:52   

Hi Otis,

I have set a breakpoint and CreatePredicate does get executed.

I have also run the SQL Server profiler to see what the database is receiving, the produced update statement does produce a where clause with the timestamp value to compare, I tested the produced update statement in query analyiser and the update does fail with a record count of zero.

PS I have also ensured the global nocount setting in the SQL Server configuration is unticked, I noticed you suggested that to someone on another thread.

But I will have a read of the DQE tracing in the docs and see what I can find out from that.

Thanks,

Sean

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 30-Nov-2005 13:55:33   

Hmm, very strange. If 0 rows are affected, the queue processor will return false, which is then returned by SaveEntity().

Frans Bouma | Lead developer LLBLGen Pro
Seanw
User
Posts: 9
Joined: 30-Nov-2005
# Posted on: 01-Dec-2005 04:45:31   

Ok I got the Concurrency exception to throw , I will explain what was happening,

The flow of my test application is,

1). Load Entity values from database, 2). Update a column in the entity, 3). Attempt to update record to database.

Between steps 1 & 2 above I change the database record outside of the application.

Now at step 2 where I update the entity column, if this value was the same as what was first read, the IsDirty property = False.

So at this stage the entities value is now different from what is now in the database record.

I now attempt to save the entity using

 adapter.SaveEntity 

The output of the DQE Trace is

Method Enter: DataAccessAdapterBase.SaveEntity(4) Method Enter: DataAccessAdapterBase.DetermineActionQueues(4) Method Exit: DataAccessAdapterBase.DetermineActionQueues(4) Method Exit: DataAccessAdapterBase.SaveEntity(4): no entities to save.

The save does not happen, but adapter.SaveEntity does returns True i.e a successful save.

Of course if I change the entity column to a new value, then attempt to save, I then get the Concurrency error.

So the question is,

Even though the stored entity has not changed, if the application did attempt to write this same record back to the database and this record has since been updated by another user, should adapter.SaveEntity return a successful save status ?

PS, I did attempt to set the entities IsDirty property = True when no change was made , This generate these DQE messages

Method Enter: DataAccessAdapterBase.SaveEntity(4) Method Enter: DataAccessAdapterBase.DetermineActionQueues(4) Method Exit: DataAccessAdapterBase.DetermineActionQueues(4) Method Enter: DataAccessAdapterBase.StartTransaction Method Enter: DataAccessAdapterBase.OpenConnection Method Exit: DataAccessAdapterBase.OpenConnection Method Exit: DataAccessAdapterBase.StartTransaction Method Enter: DataAccessAdapterBase.PersistQueue Method Exit: DataAccessAdapterBase.PersistQueue Method Enter: DataAccessAdapterBase.PersistQueue Method Enter: CreateUpdateDQ(5)

So it looks like an update to the database was attempted, but according the SQL Profiler no UPDATE statement was sent to the DB. Sean

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 01-Dec-2005 09:50:23   

Seanw wrote:

Ok I got the Concurrency exception to throw , I will explain what was happening,

The flow of my test application is,

1). Load Entity values from database, 2). Update a column in the entity, 3). Attempt to update record to database.

Between steps 1 & 2 above I change the database record outside of the application.

Now at step 2 where I update the entity column, if this value was the same as what was first read, the IsDirty property = False.

So at this stage the entities value is now different from what is now in the database record.

I now attempt to save the entity using

 adapter.SaveEntity 

The output of the DQE Trace is

Method Enter: DataAccessAdapterBase.SaveEntity(4) Method Enter: DataAccessAdapterBase.DetermineActionQueues(4) Method Exit: DataAccessAdapterBase.DetermineActionQueues(4) Method Exit: DataAccessAdapterBase.SaveEntity(4): no entities to save.

The save does not happen, but adapter.SaveEntity does returns True i.e a successful save.

aha! I was under the assumption you did change a value in the entity as you posted earlier a query was sent to the database (so I assumed an actual query was generated and executed)

Of course if I change the entity column to a new value, then attempt to save, I then get the Concurrency error.

So the question is,

Even though the stored entity has not changed, if the application did attempt to write this same record back to the database and this record has since been updated by another user, should adapter.SaveEntity return a successful save status ?

Yes, because there's nothing to save, and it was decided that a save action with nothing to save should report 'true', not 'false' which would indicate a failure. THe reason for this is that if you perform saves in a loop or in a generic routine, you probably don't want to jump out of it and handle a failure which technically isn't a failure.

Because for the 'reality' the entity in memory is in, it hasn't been changed and therefore won't be saved or better: there won't be a save action on the database.

PS, I did attempt to set the entities IsDirty property = True when no change was made , This generate these DQE messages

Method Enter: DataAccessAdapterBase.SaveEntity(4) Method Enter: DataAccessAdapterBase.DetermineActionQueues(4) Method Exit: DataAccessAdapterBase.DetermineActionQueues(4) Method Enter: DataAccessAdapterBase.StartTransaction Method Enter: DataAccessAdapterBase.OpenConnection Method Exit: DataAccessAdapterBase.OpenConnection Method Exit: DataAccessAdapterBase.StartTransaction Method Enter: DataAccessAdapterBase.PersistQueue Method Exit: DataAccessAdapterBase.PersistQueue Method Enter: DataAccessAdapterBase.PersistQueue Method Enter: CreateUpdateDQ(5)

So it looks like an update to the database was attempted, but according the SQL Profiler no UPDATE statement was sent to the DB. Sean

Correct, as no field was found which was changed so no query is generated. Please set one field's IsChanged flag to true as well and you'll see an update query. Because nothing was changed in your original attempt, the code doesn't know which UPDATE statement to generate as there's nothing to update, hence the fact that it simply returns with 'nothing to save'.

Frans Bouma | Lead developer LLBLGen Pro