Saving New Entities With Identity Primary Keys

Posts   
 
    
Jeremy Driver avatar
Posts: 41
Joined: 08-May-2004
# Posted on: 14-May-2004 20:21:16   

Hello all,

DBMS: SqlServer, Template Set: Adapter

I'm just beginning to actually use my shiny new business objects. When saving a newly created entity, it's my understanding that the primary key value generated by SqlServer is fetched automatically, regardless of the value of the 'refetchAfterSave' parameter. However, when I try to access the new entity's RowID without refetching, I get an OutOfSync exception. I understand the need to refresh in general, but I don't see why I'd need to refresh just to get the RowID value which has already been fetched. Am I missing something?

Thanks in advance, Jeremy


DoctorEntity newDoctor = new DoctorEntity();
newDoctor.LastName = "Driver";
newDoctor.FirstName = "Jeremy";
daa.SaveEntity(newDoctor, true);

DoctorEntity loadedDoctor = new DoctorEntity(newDoctor.RowID);
daa.FetchEntity(loadedDoctor);

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 14-May-2004 23:22:14   

The ID is refetched for synchronization purposes. (like an FK pointing to an identity pk in a related object). Reading the value through the property walks the normal way of retrieving a value, which will end in the state check.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 07-Jun-2005 17:44:02   

Otis wrote:

The ID is refetched for synchronization purposes. (like an FK pointing to an identity pk in a related object). Reading the value through the property walks the normal way of retrieving a value, which will end in the state check.

Are you saying that the new Identity ID which is returned (as requested by DynamicQueryEngine.CreateInsertDQ) is discarded after the saves complete?

I couldn't find your explaination of why OutOfSync is required, but I seem to remember you saying something about needing to update default values, values set by triggers etc that might be set duing the save process on the server...

I'm struggling with the fact that other than the new ID (which as far as I can tell is returned from the INSERT statement) I have no need to re-read the data. I'm not using default values or triggers.

It therfore seems logical for me to go ahead and override the setting of EntityState in SaveEntity to EntityState.Fetched again. I thought I could do this in the OnSaveEntityComplete event, but in fact this event is fired before the state is set to OutOfSync. disappointed

Now I'm thinking that maybe there is another good reason why you have not provided for this possibility, a reason that I have not thought of?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 08-Jun-2005 11:23:47   

Marcus wrote:

Otis wrote:

The ID is refetched for synchronization purposes. (like an FK pointing to an identity pk in a related object). Reading the value through the property walks the normal way of retrieving a value, which will end in the state check.

Are you saying that the new Identity ID which is returned (as requested by DynamicQueryEngine.CreateInsertDQ) is discarded after the saves complete?

No, the value is there and readable, though fields which aren't marked as the PK aren't. So if a field is marked as the PK at runtime, the value is readable.

I discovered a bug yesterday with the designer, loading an old project: PK fields weren't marked as PK anymore in the designer. I'll update the designer later this morning.

I couldn't find your explaination of why OutOfSync is required, but I seem to remember you saying something about needing to update default values, values set by triggers etc that might be set duing the save process on the server...

Correct.

I'm struggling with the fact that other than the new ID (which as far as I can tell is returned from the INSERT statement) I have no need to re-read the data. I'm not using default values or triggers.

Then that value should be available to you after a Save.

It therfore seems logical for me to go ahead and override the setting of EntityState in SaveEntity to EntityState.Fetched again. I thought I could do this in the OnSaveEntityComplete event, but in fact this event is fired before the state is set to OutOfSync. disappointed

Now I'm thinking that maybe there is another good reason why you have not provided for this possibility, a reason that I have not thought of?

In 1.0.2004.1, it was always throwing this exception, in 1.0.2004.2, it only throws this exception for non-PK fields. My unit-tests use this feature a lot, so I'll run into it whenever this changes accidently. The main thing is: after an entity is saved, the entity is marked OutOfSync, unless it's refetched. The EntityBase2.GetCurrentFieldValue() method checks for OutOfSync AND if the field is a PK field. If it's not a PK field, the exception is thrown, otherwise it's proceeding. It's therefore key the field is marked as a PK in the generated code (it normally is when it's a PK in the designer simple_smile ).

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 08-Jun-2005 16:09:01   

Otis wrote:

In 1.0.2004.1, it was always throwing this exception, in 1.0.2004.2, it only throws this exception for non-PK fields. My unit-tests use this feature a lot, so I'll run into it whenever this changes accidently. The main thing is: after an entity is saved, the entity is marked OutOfSync, unless it's refetched. The EntityBase2.GetCurrentFieldValue() method checks for OutOfSync AND if the field is a PK field. If it's not a PK field, the exception is thrown, otherwise it's proceeding. It's therefore key the field is marked as a PK in the generated code (it normally is when it's a PK in the designer simple_smile ).

Ok... that all sounds reasonable simple_smile

The following "hack" seems to work for single Entity saves... Properties are still populated and available with the same data as before the save when you force the EntitySate to "Fetched".

this.Adapter.SaveEntity(accountEntity);
accountEntity.Fields.State = EntityState.Fetched;

Unfortunately there is no similar "trick" for UnitOfWork or Collection saves because the OnSaveEntityComplete fires before the EntityState is set to OutOfSync.

I know that for a generic product it would be bad not to warn developers that an entity could be OutOfSync with the values in the DB... I wonder (other than myself) if there is a need to incorporate an override for OutOfSync (I'm thinking of v2.0 simple_smile ). In larger systems where TCP (transactions per second) is a factor, re-fetching unnecessarily is an issue...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 09-Jun-2005 12:18:34   

Marcus wrote:

Otis wrote:

In 1.0.2004.1, it was always throwing this exception, in 1.0.2004.2, it only throws this exception for non-PK fields. My unit-tests use this feature a lot, so I'll run into it whenever this changes accidently. The main thing is: after an entity is saved, the entity is marked OutOfSync, unless it's refetched. The EntityBase2.GetCurrentFieldValue() method checks for OutOfSync AND if the field is a PK field. If it's not a PK field, the exception is thrown, otherwise it's proceeding. It's therefore key the field is marked as a PK in the generated code (it normally is when it's a PK in the designer simple_smile ).

Ok... that all sounds reasonable simple_smile

The following "hack" seems to work for single Entity saves... Properties are still populated and available with the same data as before the save when you force the EntitySate to "Fetched".

this.Adapter.SaveEntity(accountEntity);
accountEntity.Fields.State = EntityState.Fetched;

Unfortunately there is no similar "trick" for UnitOfWork or Collection saves because the OnSaveEntityComplete fires before the EntityState is set to OutOfSync.

Though PK values SHOULD be readable after a UoW or collection save even though you didn't refetch. Isn't that the case in your situation?

I know that for a generic product it would be bad not to warn developers that an entity could be OutOfSync with the values in the DB... I wonder (other than myself) if there is a need to incorporate an override for OutOfSync (I'm thinking of v2.0 simple_smile ). In larger systems where TCP (transactions per second) is a factor, re-fetching unnecessarily is an issue...

true, that's why it's optional simple_smile . But doesn't it work for you to have the PK values available?

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 09-Jun-2005 13:38:36   

Otis wrote:

Though PK values SHOULD be readable after a UoW or collection save even though you didn't refetch. Isn't that the case in your situation?

I require the PKs and all the other fields to be available...

In my app, a request (in the form of a business action) comes in and a transaction is started in the Bus Process layer. The request is serviced by the Bus Process and lower layers... A UnitofWork is passed between layers to collect changes to the db as a result of the business processing..

Service Request -> Bus. Process -> Bus. Activity -> Data Layer -> Database Service Response <- Bus. Process <- Bus. Activity <- Data Layer <-

The UnitOfWork / transaction is then commited in the Bus Process layer (where is was previously begun) and the response is formulated after the transction succesfully commits. It's a this point that Entities are mapped to schema types to push back out over the wire. But some of the entities have been saved now and are OutOfSync, are they not? Hence I have to force their Fields.State = EntityState.Fetched to avoid an OutOfSync exception.

Otis wrote:

true, that's why it's optional simple_smile . But doesn't it work for you to have the PK values available?

Shouldn't it be optional for all fields, not just the PKs?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 09-Jun-2005 14:14:39   

I don't think it should be optional for all fields, as it is incorrect.

Though if you REALLY want it, you can derive a class from DataAccessAdapter and override SaveEntity(). In there, simply call the base method, and if true is returned, set the state to Fetched.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 09-Jun-2005 14:58:06   

Otis wrote:

Though if you REALLY want it, you can derive a class from DataAccessAdapter and override SaveEntity(). In there, simply call the base method, and if true is returned, set the state to Fetched.

Amazing... I hadn't even thought of that!!! smile

Speaking of subclassing DataAccessAdapter did you have any thoughts on changing the protection level of:

private FetchPrefetchPath -> protected virtual private MergeNormal -> protected private MergeManyToMany -> protected

... in DataAccessAdapterBase to accomodate my custom paging + prefetch (from our other discussion on http://llblgen.com/tinyforum/Messages.aspx?ThreadID=1517&StartAtMessage=34)

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 09-Jun-2005 18:25:38   

Marcus wrote:

Otis wrote:

Though if you REALLY want it, you can derive a class from DataAccessAdapter and override SaveEntity(). In there, simply call the base method, and if true is returned, set the state to Fetched.

Amazing... I hadn't even thought of that!!! smile

Speaking of subclassing DataAccessAdapter did you have any thoughts on changing the protection level of:

private FetchPrefetchPath -> protected virtual private MergeNormal -> protected private MergeManyToMany -> protected

... in DataAccessAdapterBase to accomodate my custom paging + prefetch (from our other discussion on http://llblgen.com/tinyforum/Messages.aspx?ThreadID=1517&StartAtMessage=34)

MergeNormal and MergeManyToMany not virtual? I don't mind making them protected virtual simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 09-Jun-2005 18:53:50   

Otis wrote:

MergeNormal and MergeManyToMany not virtual? I don't mind making them protected virtual simple_smile

Continued over on http://llblgen.com/tinyforum/Messages.aspx?ThreadID=1517&StartAtMessage=25&#18670 ...

Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 02-Nov-2005 16:54:29   

Marcus wrote:

Otis wrote:

Though if you REALLY want it, you can derive a class from DataAccessAdapter and override SaveEntity(). In there, simply call the base method, and if true is returned, set the state to Fetched.

Amazing... I hadn't even thought of that!!! smile

OutOfSync strikes AGAIN... this time in UnitOfWork2 when it calls DataAccessAdapterBase.PersistQueue().

Honestly... if you know that the only changes to your data will be an updated ID (which is refetched via the output param anyway) then refetching is not necessary and should be optional.

I would really like to introduce a property to DataAccessAdapterBase called "MarkEntitiesOutOfSyncAfterSave" which has a default value of true.

Then

entityToSave.Fields.State=EntityState.OutOfSync;

becomes


if (_markEntitiesOutOfSyncAfterSave)
    entityToSave.Fields.State=EntityState.OutOfSync;
else
    entityToSave.Fields.State=EntityState.Fetched;

Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 03-Nov-2005 08:46:51   

Actually Frans, I was thinking about this some more... Although not mandating a refretch after save is still required IMO, the above "solution" is a hack flushed

Of course you still want to mark the entities as "OutOfSync" (because they are) but what I actually want is for this state not to impede access to the entity's properties.

So the change needs to happen in EntityBase2.GetCurrentFieldValue. This complicates things as now there is an issue as to where the flag to "check or not to check" the fields.State should be stored...

I know you are loath to make this kind of change! And I know the reason for the State check is to protect the developer against potentially stale data, but some of us know what were doing stuck_out_tongue_winking_eye

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 03-Nov-2005 09:35:58   

Marcus wrote:

Actually Frans, I was thinking about this some more... Although not mandating a refretch after save is still required IMO, the above "solution" is a hack flushed

Of course you still want to mark the entities as "OutOfSync" (because they are) but what I actually want is for this state not to impede access to the entity's properties.

So the change needs to happen in EntityBase2.GetCurrentFieldValue. This complicates things as now there is an issue as to where the flag to "check or not to check" the fields.State should be stored...

I know you are loath to make this kind of change! And I know the reason for the State check is to protect the developer against potentially stale data, but some of us know what were doing stuck_out_tongue_winking_eye

"but some of us know what were doing :P" haha Famous Last Words(TM) wink simple_smile

I know what you mean. Though you can easily solve it: override OnTransactionCommit in each entity, and set the state there, e.g.: if OutOfSync -> Fetched.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 05-Nov-2005 12:03:41   

Otis wrote:

"but some of us know what were doing :P" haha Famous Last Words(TM) wink simple_smile

smile

Otis wrote:

I know what you mean. Though you can easily solve it: override OnTransactionCommit in each entity, and set the state there, e.g.: if OutOfSync -> Fetched.

OK... but this a hack too! disappointed as the entity IS OutOfSync. I simply think that property access of an OutOfSync entity is okay in particular circumstances...

This is okay for me, but I do think this is an area which could be improved for your 2.0 release.

Have a good weekend,

Marcus

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 05-Nov-2005 12:59:35   

Marcus wrote:

Otis wrote:

"but some of us know what were doing :P" haha Famous Last Words(TM) wink simple_smile

smile

Otis wrote:

I know what you mean. Though you can easily solve it: override OnTransactionCommit in each entity, and set the state there, e.g.: if OutOfSync -> Fetched.

OK... but this a hack too! disappointed as the entity IS OutOfSync. I simply think that property access of an OutOfSync entity is okay in particular circumstances...

This is okay for me, but I do think this is an area which could be improved for your 2.0 release.

You mean with a setting which says: "ignore outofsync" ? Because by definition, an entity which is outofsync is... outofsync, so the exception IMHO is reasonable. For PK reads, this isn't the case, PKs generated by the DB are already propagated back and you can read them after the save, no matter what. The other fields are outofsync and have to be refetched. I'm a bit puzzled how to 'solve' this, because any other solution than throwing an exception is in fact simply throwing away the whole concept of having an out-of-sync entity...

Have a good weekend, Marcus

You too simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 06-Nov-2005 13:21:54   

Otis wrote:

You mean with a setting which says: "ignore outofsync" ?

Exactly.

Otis wrote:

Because by definition, an entity which is outofsync is... outofsync, so the exception IMHO is reasonable. For PK reads, this isn't the case, PKs generated by the DB are already propagated back and you can read them after the save, no matter what. The other fields are outofsync and have to be refetched. I'm a bit puzzled how to 'solve' this, because any other solution than throwing an exception is in fact simply throwing away the whole concept of having an out-of-sync entity...

I think this is what we are debating simple_smile

Is accessing an OutOfSync entities properties really an error? I think throwing an exception is very harsh, because it leaves the developer with no way to turn except a refetch. There are very real scenarios where the refetch is never required.

In my particular application which is a web service... a web service call (a Business Action) arrives is processes by the application. The business action performs reads and writes to the database and often returns information in the response. The information returned in the response is more than likely the data contained in the saved entities which are now "OutOfSync". Depending on how you have designed your database schema and your concurrancy protection this may or may not present a problem. For performance, you obviously would not want to refetch the same data simple because it is marked as OutOfSync. Even if concurrancy is a problem, you implementation may not care...

There are of course other situations where you would want to refetch stale data so having the OutOfSync state is definately a good thing...

In summary I thing the OutOfSync state is a warning rather than an error and I don't believe warnings should throw exceptions unless they are configured to do so... That's why I think having a property which can switch on and off the exception would be very usefull. Of course you could default to "true" for backward compatability.

Marcus