IConcurrencyPredicateFactory difference when using Save() vs. Save(true)

Posts   
 
    
djmarkert
User
Posts: 4
Joined: 23-Nov-2004
# Posted on: 16-Mar-2005 15:36:20   

I've implemented a class that inherits from IConcurrencyPredicateFactory to perform concurrency control when updating an entity per the help manual instructions. I've setup a small test to verify that the concurrency control is working by writing 2 methods as follows:

Method1 gets entity A Method1 calls Method2 Method2 gets entity A and sets a simple property on the entity to a different value than what was read in. Method2 saves entity A using entityA.Save() Method1 sets the same simple property to a different value than what was read in. Method1 saves entity A using entityA.Save()

I'm findiing that unless I use the save overload that takes the recursive flag, i.e. .Save(true), I don't get a ORMConcurrencyException when Method1 attempts to save the entity. Is this to be expected?

The end result when both methods use .Save() is that the value in the db for the simple property I'm changing is the value saved by Method2. Also, the .Save() in Method1 returns false.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 16-Mar-2005 16:05:31   

There was a bug in Save() which indeed skipped the factory call. Please download the latest runtime libraries archive and see if that fixes your problem.

Frans Bouma | Lead developer LLBLGen Pro
djmarkert
User
Posts: 4
Joined: 23-Nov-2004
# Posted on: 16-Mar-2005 17:03:17   

I'm seeing the same behavior in the latest runtimes (SD.LLBLGen.Pro.ORMSupportClasses.NET11.dll version 1.0.20041.50314, file date 3/16/2005). I'm using Self-Servicing with the Sql driver.

Here's the code I'm using:


public void Method1()
{
  PerspectiveRecordEntity pre = new PerspectiveRecordEntity(8);

  try
  {
    pre.ConcurrencyPredicateFactoryToUse = new PerspectiveConcurrencyFilterFactory();
    
    Method2();
    pre.Parent = pre.Parent + 1;
    
    bool bResult = pre.Save();      //this save returns false
  }
  catch(Exception ex)
  {
    //handle error
  }
}


public void Method2()
{
  PerspectiveRecordEntity pre = new PerspectiveRecordEntity(8);
            
  try
  {
    pre.ConcurrencyPredicateFactoryToUse = new PerspectiveConcurrencyFilterFactory();
    pre.Parent = pre.Parent - 1;
    bool bResult = pre.Save();                //this save returns true
  }
  catch(Exception ex)
  {
    //handle error
  }
}

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 16-Mar-2005 20:41:57   

I'll look into it.

Frans Bouma | Lead developer LLBLGen Pro
djmarkert
User
Posts: 4
Joined: 23-Nov-2004
# Posted on: 17-Mar-2005 01:36:12   

Otis,

I found the following information in the 'Handling Exceptions' topic in the help manual which sheds light on this issue:

ORMConcurrencyException. This exception is thrown when, during a recursive save, a save fails, i.e. doesn't affect any rows. This can be caused by the fact that the row being updated is already deleted, or the predicate created by the set ConcurrencyPredicateFactory or the passed in predicate caused a failure of the update due to a concurrency violation. This exception is only thrown during a recursive save. If a save is not recursive, the save just fails and false is returned. This is done for backwards compatibility, as this exception is introduced after the development of recursive saves. The exception will terminate the transaction started during a recursive save and will therefore make a recursive save action completely atomic.

Well, that would explain the behavior I'm seeing disappointed .

Is the need for backward compatibility really that strong to keep the .Save() working as is (returning false)?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 17-Mar-2005 13:22:14   

djmarkert wrote:

Otis,

I found the following information in the 'Handling Exceptions' topic in the help manual which sheds light on this issue:

ORMConcurrencyException. This exception is thrown when, during a recursive save, a save fails, i.e. doesn't affect any rows. This can be caused by the fact that the row being updated is already deleted, or the predicate created by the set ConcurrencyPredicateFactory or the passed in predicate caused a failure of the update due to a concurrency violation. This exception is only thrown during a recursive save. If a save is not recursive, the save just fails and false is returned. This is done for backwards compatibility, as this exception is introduced after the development of recursive saves. The exception will terminate the transaction started during a recursive save and will therefore make a recursive save action completely atomic.

Well, that would explain the behavior I'm seeing disappointed .

Is the need for backward compatibility really that strong to keep the .Save() working as is (returning false)?

Yes, as changing this would break a lot of applications because all of a sudden they'll get an exception instead of a return value. This is an old debate: return values vs. exceptions, and I'm in teh camp which says that exceptions shouldn't be used for flow control, but should be used to inform the caller code of an exceptional (hence the name IMHO wink ) situation. The save() also fails if no concurrencypredicatefactory is used by the record is deleted for example from the database.

With a non-recursive Save, and it fails, you know which entity failed, the one you called Save() on. In a recursive save, you can't say that. Also the recursive save is always run inside a transaction, so to make that rollback the inner save somewhere deep in the recursion has to throw an exception.

So rule of thumb: always check return values and act accordingly, and recursive saves throw exceptions because of transactional rollback/commit issues.

Frans Bouma | Lead developer LLBLGen Pro