Global Concurrency Implementation

Posts   
 
    
Alfredo avatar
Alfredo
User
Posts: 46
Joined: 12-Dec-2004
# Posted on: 17-Jun-2006 22:44:07   

What would be the best way to implement a global concurrency implementation on a project.

I know how to create the Concurrency Predicate for each entity, but I was wondering if there is an easier way to implement it globally. I have 500+ entities and all of them have a version number, so the implementation should be the same for each entity, and I would prefer to have a single definition instead of 500+ factories.

Thanks

Alfredo

takb
User
Posts: 150
Joined: 12-Mar-2004
# Posted on: 18-Jun-2006 03:12:57   

I've just been through this exercise myself.

The short answer is as follows:

  1. write a "DefaultConcurrencyPredicateFactory" class which is the concurrency predicate factory that you want to use for all of your entites. Implement this as a custom template and include it appropriately in the LLBGen Pro code generation process (add a task and create a custom preset in v2.0). ie we just need to make our global or default concurrency predicate factory implementation available to all of the entities. Make sure this class is in a namespace referenced by the entity classes (I put mine in namespace <[RootNamespace]>.HelperClasses).

  2. create a custom Entity Initialization Adapter Include template (if you're using Adapter). This adds code to the class initialiser that is run whenever a new instance of the entity is created. This template should have the following line:

ConcurrencyPredicateFactoryToUse = new DefaultConcurrencyPredicateFactory();

And that's it!

There is one other step that I have done to simplify a special case that you may have to deal with: sometimes you want to update an entity without first fetching it - and still have optimistic concurrency control work. For example, you present an entity on a web page for a user to edit. The user edits that record and saves it. One of the techniques you can employ is to save the entity ID and row version in Viewstate (or session). On Postback you create a new entity but set it's properties to the values provided by the user. You also set the PK and rowVersion from the values that you saved in Viewstate (or session). You then mark the entity as IsNew=false. You can then save the entity and it will update the existing entity. The trick here is that for concurrency control to work properly (if you implement control the standard way by testing DbValue), you actually have to set the DbValue of the row version property, not CurrentValue. That is, the entity must think that the "old" value of row version was retrieved from the database. But DbValue is readonly so you have to use a special setting method called ForcedCurrentValueWrite. So to simplify this I also create an entity adapter include template to add an additional constructor to my entity as follows. Then in this special case of updating an entity without first fetching it, I can construct it with: new Entity(Pk, RowVersion) and everything is set up for me.

        /// <summary> CTor</summary>
        /// <param name="iD">PK value for <[CurrentEntityName ]> which data should be fetched into this <[ CurrentEntityName ]> object</param>
        /// <param name="rowVersion">Concurrency Control field for this entity. Providing this value will set the underlying DbValue property. Used for updating entities without fetching first</param>
        /// <remarks>The entity is not fetched by this constructor. Use a DataAccessAdapter for that.</remarks>
        public <[ CurrentEntityName ]>Entity(System.Int32 iD, System.Int32 rowVersion):this(iD)
        {
            // Assign both the current value and the DbValue of ROWLOCKKEY. This is used to maintain concurrency control when updating entities 
            // without first fetching that entity.
            this.Fields[(int)<[ CurrentEntityName ]>FieldIndex.ROWLOCKKEY].ForcedCurrentValueWrite(rowVersion, rowVersion);
            this.IsNew = false;
        }

Sorry if this post is a little terse or not clear in explanation. I haven't got a lot of time at the moment and my wife is badgering me to get off the computer wink but I thought this info might be useful to you... If you have any more questions, just ask and I'll explain a little better when I have some more time wink

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 19-Jun-2006 11:25:49   

Also keep in mind that you can refer to a field by its name. So in your concurrencypredicatefactory, if you want to refer to the field 'TimeStmp', and compare to its dbvalue, you can simply index in the passed in entity's Fields collection using the string "TimeStmp". You then just have to make sure every entity you want to use the concurrencypredicatefactory on has to have the same name for the field used for concurrencychecking. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Alfredo avatar
Alfredo
User
Posts: 46
Joined: 12-Dec-2004
# Posted on: 27-Jun-2006 07:15:16   

Thanks Frans.

Sorry I didnĀ“t answer before. I've been caught with the World Cup lately. Sorry for The Netherlands.

Anyway, thanks for the suggestions. I'll try them and I will let you know how it goes.

Thanks again for you prompt and clear response

Alfredo