concurrency based on another table value

Posts   
 
    
sunnyman
User
Posts: 51
Joined: 21-Feb-2007
# Posted on: 06-Jan-2011 19:23:42   

Hi,

I have 2 tables (Account & Service) Account (AccountID, ISClosed) Service (ServiceID, AccountID (FK), …..) They have 1 to many relationship (Account -> Service) I can add service only if the account is open. How can I save ServiceEntity Only if the account is open( without selecting from table account inside transaction)?

For Example
Begin Transaction
Select From Account where AccountID = 1 AND  IsClosed =1
Service.AccountID =1 
Service.SAVE
END Transaction

I need something like this

Begin Transaction
Service.AccountID =1
Service.Save (Only IF Account.IsClosed = false)
End Transaction

Any clean Ideas to do this? Thanks

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 06-Jan-2011 22:03:56   

Is concurrency seriously likely to be a problem in a situation like this? Is it really possible that someone will have closed the account between you checking if it is open, and saving the service ?

I'll have a think about a way to do this in the mean time.

Matt

sunnyman
User
Posts: 51
Joined: 21-Feb-2007
# Posted on: 06-Jan-2011 23:00:45   

Hi,

Concurrency is always very important in all situations if you hope to build stable & accurate applications.

Is it really possible that someone will have closed the account between you checking if it is open

How could I check it? That is my question simple_smile

If you mean , I check it when I open the service screen / form, then I can’t depend on this because I don’t know how much time between opening the form and saving it.

If you mean, I check it inside transaction with repeatable read, then this is always not recommended specially in busy systems. And my question is how to avoid this if I can.

**By the way, this should be a common situation in most business applications ** For example

  • Checking Credit Limit (max amount for a client to purchase by credit) for client before creating credit sales invoice for him. Specially if you have multi physical stores

  • This also could happen in a pointing system common in POS (Point of Sales) where the buyer takes points in every purchase and later it could get a discount depending of these points

  • In my situation, the account table represents a medical account in hospitals. In busy day, it could happen (and it really happen) that the nurse try to add service (Operation Materials) on the account, while the accountant have already closed the account to calc patient invoice.

IN all the above situations, The Credit Limit, Points per customer, Account per Patient are stored in master tables while the transaction stored in another one. You must check them on every save for any transaction.

Thanks.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 07-Jan-2011 05:51:18   

This can't be done at Entity level, as you can't use related entities on IConcurrencyPredicateFactory, this is because you can't pass relations to it nor adapter.SaveEntity.

As a workaround you could use adapter.UpdateEntitiesDirectly and pass to it the values to update, the involved relation (Account -> Service), and your custom filter restriction (AccountFields.IsClosed == true). Its important you pass the entity filter (Service.ServiceId == theId). Some approximate example:

// fetch your entity
ServiceEntity service = new ServiceEntity(123);
adapter.FetchEntity(service);

// the changes...
service.SomeField = newValue;
...

// the update restriction
IRelationPredicateBucket filter = new RelationPredicateBucket();
filter.Add(ServiceFields.ServiceId == service.ServiceId);
filter.Add(Account.IsClosed == true);
filter.Relations.Add(ServiceEntity.Relations.AccountEntityUsingServiceId);

// save
adapter.UpdateEntitiesDirectly(service, filter);

In short, this code will update your specific database row with your changed values, only if its associated account is closed.

David Elizondo | LLBLGen Support Team
sunnyman
User
Posts: 51
Joined: 21-Feb-2007
# Posted on: 07-Jan-2011 16:26:41   

Thanks,

I think this workaround should be sufficient.