How to solve concurrency for parent/child class

Posts   
 
    
KIT
User
Posts: 59
Joined: 04-Apr-2007
# Posted on: 23-Apr-2007 09:28:53   

LLBLGen v.2.0.0.0 / .NET 2

Hi,

I have a base class Person and a sub class Employee. Both classes / tables have a timestamp field in order to handle concurrency. I want to have it in this way so that I can use a generic ConcurrencyPredicateFactory (all classes must implement the timestamp field). This combination leads to a compiling error because both, the parent and child class, implement the timestamp field.

So apparently my idea to have a timestamp field in every class and to use a generic ConcurrencyPredicateFactory does not work. disappointed

What is the right solution to solve that problem? How can I implement a generic ConcurrencyPredicateFactory? Shouldn't it be the case that LLBLGen updates the Employee timestamp when updating fields of the Person object? Because in code I write adatpter.update(employee). So as a programmer I can't see that in background only the table person is updated and the timestamp of employee is not getting changed.

Thanks for your help!

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 23-Apr-2007 09:46:17   

Since a sub class/entity inherits its fields from the super class/entity, you don't neeed the timestamp on the employee table/entity. Remove it and keepthe one in the Person table.

KIT
User
Posts: 59
Joined: 04-Apr-2007
# Posted on: 23-Apr-2007 10:48:09   

Thanks for your fast reply. I tried what you recommended. However, it doesn't work as it should. The problem is the following:

Lets assume we have the following classes / properties: Person.Name (parent class) Employee.Department (child class)

On the level of SQL Server I defined a timestamp column for person with the datatype timestamp. So SQL Server updates this column by itselfs if updates are written to table person.

Now I'm gonna update Employee.Department --> The timestamp field in Person is not changed! So now another user can update Employee.Department as well without running into a concurrency exception. confused

What is the right approach to solve that problem?

jbb avatar
jbb
User
Posts: 267
Joined: 29-Nov-2005
# Posted on: 23-Apr-2007 14:55:26   

Hello,

the best way is to used your own date/time column because only llblgen manage inheritance and not sql. So sql won't update the time stamp column like you want.

KIT
User
Posts: 59
Joined: 04-Apr-2007
# Posted on: 23-Apr-2007 15:55:05   

So you mean I should add a date/time colum to the parent table and every time I update an entity I have to update this column? Like this:


myEntity.timestamp = DateTime.Now;
adapter.saveEntity(myEntity)

I think that would work but it's not a very generic solution. Is there something like an "onBeforeSave" method, in which I could set the timestamp?

jbb avatar
jbb
User
Posts: 267
Joined: 29-Nov-2005
# Posted on: 23-Apr-2007 16:34:26   

Hello,

that what I thought about. You can also overrides the OnBeforeEntitySave method to add the current date/time each time the entity is modified.

KIT
User
Posts: 59
Joined: 04-Apr-2007
# Posted on: 23-Apr-2007 17:47:16   

It doesn't work with OnBeforeEntitySave. rage

The problem is that the ConcurrencyPredicateFactory is called BEFORE "OnBeforeEntitySave".

So in my case I have a Person class (parent) and a Employee class (child). If I only change fields in the Employee object, there is no ConcurrencyPredicateFactory added to the Person object and therefore the timestamp is not checked.

Is there another method like "OnBeforeEntitySave" which is called before the PredicateExpression is fetched from the ConcurrencyPredicateFactory? I wasn't able to find something in the manual.. confused

How would you solve that?

KIT
User
Posts: 59
Joined: 04-Apr-2007
# Posted on: 24-Apr-2007 09:21:55   

I solved the problem. simple_smile There was a mistake at another place in my code.. Thanks again for your help. My code is below. Maybe others can use it. It's a fully generic solution. The only thing you have to do in order to enable concurrency control for a specific entity is to add a timestamp field to the corresponding table in the database.

ConcurrencyPredicateFactory

[Serializable]
public class ConcurrencyPredicateFactory : IConcurrencyPredicateFactory  {
        private EntityField2 mTimestampField;
        
        // constructor
        public ConcurrencyPredicateFactory(IEntityField2 timestampField) {
            mTimestampField = (EntityField2)timestampField;
        }

        public IPredicateExpression CreatePredicate(
                ConcurrencyPredicateType predicateTypeToCreate, 
                object containingEntity) {

            IPredicateExpression toReturn = new PredicateExpression();
            EntityBase2 entity = (EntityBase2)containingEntity;

            toReturn.Add(mTimestampField == entity.Fields["Timestamp"].DbValue);

            return toReturn;
        }
}

In Template Studio, add the following lines to the entityIncludeAdapter.template

protected override void OnBeforeEntitySave() {
            base.OnBeforeEntitySave();

            IEntityField2 timestampField = this.Fields["Timestamp"];
            if (timestampField != null)
                timestampField.CurrentValue = DateTime.Now;
}

In the same template as described above, look for the method InitClassEmpty(...) and add the following code to it:


IEntityField2 timestampField = this.Fields["Timestamp"];
if (timestampField != null)
                ConcurrencyPredicateFactoryToUse = new ConcurrencyPredicateFactory(timestampField);

To enable concurrency control for an entity just add a Timestamp field to the corresponding table in the database (datatype Datetime). If you have derived classes only add the Timestamp field to the root/base class. That's all.. smile

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 24-Apr-2007 09:23:46   

Thanks for the feedback

itPhoenix
User
Posts: 11
Joined: 03-May-2007
# Posted on: 23-Jul-2007 19:54:03   

Hello, I just recently purchased llblgenpro and am looking to implement this myself (Sql Server 2005). I have one problem with the solution presented here:

To enable concurrency control for an entity just add a Timestamp field to the corresponding table in the database (datatype Datetime). If you have derived classes only add the Timestamp field to the root/base class. That's all.. smile

It was my belief, and perhaps someone could correct me if I am mistaken, but using an actual DateTime for concurrency was a big no-no. Shouldn't the timestamp datatype be used (as you stated at the beginning)?

I would use a timestamp dataType and a datetime dataType (I happen to place this in every table in my schema except for 'sub-class' tables such as Employee (is-a Person) anyway.)

For generalization tables (e.g. Person), update a datetime column (dateTime dataype) that is also present and only in the 'super-class' table. This could be done in on-beforeEntity save and would force the timestamp column, which is present in the Person table , to refresh itself).

Am I on the right track here confused As I am new to the product, I need to know before I am off and coding! Thanks!

KIT
User
Posts: 59
Joined: 04-Apr-2007
# Posted on: 24-Jul-2007 08:57:09   

Hi

Did you extend your code as described? There are 3 extensions you have to implement:

  1. Implement your own PredicateFactory
  2. Extend the entity template as described (this is pretty complex to do) - Two extensions are required. Use Template Studio to implement these extensions.
  3. Add Timestamp (datatype DateTime) field to your table. I guess it should work with Timestamp as datatype as well. But I haven't tried that.

Having that done should enable concurrency control for your application. simple_smile

Cheers.

itPhoenix
User
Posts: 11
Joined: 03-May-2007
# Posted on: 24-Jul-2007 23:44:02   

Thanks KIT, I will try it. I was more or less asking a best practice question also....... Should a datetime ever be used for concurrency checking?

I think the code will work great, I will just use a timestamp instead of a datetime (except for sub-types; I will update the parent entity 'dateUpdated' which will cause the timestamp to 'roll' automatically).

Thanks,