A scenario where save doesn't work?

Posts   
 
    
happyfirst
User
Posts: 215
Joined: 28-Nov-2008
# Posted on: 28-Apr-2009 22:31:02   

Self-Servicing 2.6 - Oracle

Create an entity with user supplied primary key (one or more) that cannot be null. Make all other fields nullable. Use parameterized constructor to create entity Immediately call Save.

Run a test where you supply a null value for one of the primary keys. (My scenario had a three column primary key entity, all varchars) No exception is thrown. Shouldn't an exception be thrown?

I found a workaround which was to set primary keys after calling parameterless constructor. I then wondered if I had set the primary key properly in the original scenario, would the object still have saved?

rdhatch
User
Posts: 198
Joined: 03-Nov-2007
# Posted on: 29-Apr-2009 00:39:31   

Happy! Good to see you over here, my good Visual WebGui friend.

I'm sure Frans & the Support Team will be here soon to help.

Ryan

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 29-Apr-2009 09:18:40   

happyfirst wrote:

Self-Servicing 2.6 - Oracle Create an entity with user supplied primary key (one or more) that cannot be null. Make all other fields nullable. Use parameterized constructor to create entity Immediately call Save.

Run a test where you supply a null value for one of the primary keys. (My scenario had a three column primary key entity, all varchars) No exception is thrown. Shouldn't an exception be thrown?

I found a workaround which was to set primary keys after calling parameterless constructor. I then wondered if I had set the primary key properly in the original scenario, would the object still have saved?

When you do this: CustomerEntity c = new CustomerEntity("ALFKI");

it will create a new entity class instance of type CustomerEntity and will try to fetch the entity with PK "ALFKI" into it. It won't set the PK field as being 'changed' because, if ALFKI doesn't exist and you want to get rid of the entity, it's then still 'dirty' (has a changed field) and therefore would cause phantom inserts.

So if you want to create a new entity in selfservicing and set it's PK, do: CustomerEntity c = new CustomerEntity(); c.CustomerId = "ALFKI";

i.o.w: set the field like any other field.

No exception is thrown when save fails, as the save doesn't really 'fail', there's just nothing changed in the entity object, so there's nothing to save, hence the silent save (Save() will return 'false' in that case)

Frans Bouma | Lead developer LLBLGen Pro
happyfirst
User
Posts: 215
Joined: 28-Nov-2008
# Posted on: 29-Apr-2009 14:50:45   

rdhatch wrote:

Happy! Good to see you over here, my good Visual WebGui friend.

How's it going? I'm not doing much VWG anymore. Just got tired of all the issues and bs. I'm working on a silverlight framework now.

happyfirst
User
Posts: 215
Joined: 28-Nov-2008
# Posted on: 29-Apr-2009 14:54:56   

Otis wrote:

When you do this: CustomerEntity c = new CustomerEntity("ALFKI");

it will create a new entity class instance of type CustomerEntity and will try to fetch the entity with PK "ALFKI" into it. It won't set the PK field as being 'changed' because, if ALFKI doesn't exist and you want to get rid of the entity, it's then still 'dirty' (has a changed field) and therefore would cause phantom inserts.

I don't understand this scenario. I called new constructor with some PKs, nothing comes back, I still have a valid "isNew" object, now I want to get rid of the entity. What does "get rid of" mean? Rid of from memory, from the database? From mem, I could just let GC collect it? From db, if I called delete, there's nothing to delete? How does a phantom insert occur? I couldn't even get it to save?

Otis wrote:

So if you want to create a new entity in selfservicing and set it's PK, do: CustomerEntity c = new CustomerEntity(); c.CustomerId = "ALFKI";

i.o.w: set the field like any other field.

That is what I am now doing.

Otis wrote:

No exception is thrown when save fails, as the save doesn't really 'fail', there's just nothing changed in the entity object, so there's nothing to save, hence the silent save (Save() will return 'false' in that case)

In my case, regardless of whether I set all the primary keys to a valid value or not, Save always returns TRUE, not false. That doesn't seem right. Nothing got saved.

I find loading an existing object by calling a new constructor confusing. I don't understand why there isn't just a static Load method that returns null if no object is found. If null and I wanted to create, then I would use the new constructor to create it and save it. The first client I presented llbl to also found that confusing.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 29-Apr-2009 17:13:21   

happyfirst wrote:

Otis wrote:

When you do this: CustomerEntity c = new CustomerEntity("ALFKI");

it will create a new entity class instance of type CustomerEntity and will try to fetch the entity with PK "ALFKI" into it. It won't set the PK field as being 'changed' because, if ALFKI doesn't exist and you want to get rid of the entity, it's then still 'dirty' (has a changed field) and therefore would cause phantom inserts.

I don't understand this scenario. I called new constructor with some PKs, nothing comes back, I still have a valid "isNew" object, now I want to get rid of the entity. What does "get rid of" mean? Rid of from memory, from the database? From mem, I could just let GC collect it? From db, if I called delete, there's nothing to delete? How does a phantom insert occur? I couldn't even get it to save?

If you don't get an entity fetched even if the PK is valid, please enable DQE tracing and check the query produced to see if the query indeed is correct and passes the proper value.

Getting rid of means that you don't want the entity object to stay around. This is a problem in some edge case scenario's if the entity is in a collection, and you want to save the entities that are changed but not this particular one. As it's not changed at all, it's not saved, however if the PK fields were always set regularly through this CTor, the entity would always be dirty and ALWAYS be saved, so you would actively have to hunt for it and remove it from the collection. See it as a convenience choice: not setting the PK fields this way saves you a lot of trouble but of course you have to set them manually, setting them regularly will potentially cause you problems in the other way around in edge cases (and these are always only found through testing (read: in production))

Otis wrote:

No exception is thrown when save fails, as the save doesn't really 'fail', there's just nothing changed in the entity object, so there's nothing to save, hence the silent save (Save() will return 'false' in that case)

In my case, regardless of whether I set all the primary keys to a valid value or not, Save always returns TRUE, not false. That doesn't seem right. Nothing got saved.

Correct, my bad, I should have checked the spec instead of digging from memory flushed It returns false if the save failed for some reason.

I find loading an existing object by calling a new constructor confusing. I don't understand why there isn't just a static Load method that returns null if no object is found. If null and I wanted to create, then I would use the new constructor to create it and save it. The first client I presented llbl to also found that confusing.

The new CTor fetching is a convenience route for selfservicing, for people who like to use 'new' instead of factory methods. A different route exists though: CustomerEntity c = new CustomerEntity(); bool fetchResult = c.FetchUsingPK("PKValue");

if the call indeed resulted in a fetch of an entity, fetchResult is true, otherwise it's false. If it's false, the entity isn't in a Fetched state, but still 'new'. FetchUsingPK also has overloads for prefetch paths etc.

It's a little bit more code over the CTor route (not much) so it's up to you what you'd pick. Do you consider this less confusing?

Frans Bouma | Lead developer LLBLGen Pro
happyfirst
User
Posts: 215
Joined: 28-Nov-2008
# Posted on: 29-Apr-2009 17:31:30   

Otis wrote:

If you don't get an entity fetched even if the PK is valid, please enable DQE tracing and check the query produced to see if the query indeed is correct and passes the proper value.

The PK's are valid values but the object does NOT exist in the database and it's working as expected. I'm wanting to create a new record.

Otis wrote:

Correct, my bad, I should have checked the spec instead of digging from memory flushed It returns false if the save failed for some reason.

Again, my point is that I called new parameterized constructor passing in valid pk values (pks for an object that does NOT exist in the database). The objects was NOT dirty but it was isNew=true. I then called saved.

It returned TRUE but it did NOT save anything. Shouldn't it have returned FALSE?

Otis wrote:

The new CTor fetching is a convenience route for selfservicing, for people who like to use 'new' instead of factory methods. A different route exists though: CustomerEntity c = new CustomerEntity(); bool fetchResult = c.FetchUsingPK("PKValue");

if the call indeed resulted in a fetch of an entity, fetchResult is true, otherwise it's false. If it's false, the entity isn't in a Fetched state, but still 'new'. FetchUsingPK also has overloads for prefetch paths etc.

It's a little bit more code over the CTor route (not much) so it's up to you what you'd pick. Do you consider this less confusing?

I personally think it would make more sense if FetchUsingPK was static, returned the object if found and returned null if no object was found.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 30-Apr-2009 10:08:35   

happyfirst wrote:

Otis wrote:

If you don't get an entity fetched even if the PK is valid, please enable DQE tracing and check the query produced to see if the query indeed is correct and passes the proper value.

The PK's are valid values but the object does NOT exist in the database and it's working as expected. I'm wanting to create a new record.

Ah, I now understand, I understood from your previous posts that you tried to fetch an existing entity from the db

The CTor with the PK fields is for fetching, not for creating a new entity. As I explained, it's a convenience method. We also could have added a CTor with all the fields to create a new entity with those values. We didn't, it's a choice between what's possible.

Confusion comes from the fact that you expected A but the ctor is meant to do B. B is clearly documented. wink

Otis wrote:

Correct, my bad, I should have checked the spec instead of digging from memory flushed It returns false if the save failed for some reason.

Again, my point is that I called new parameterized constructor passing in valid pk values (pks for an object that does NOT exist in the database). The objects was NOT dirty but it was isNew=true. I then called saved. It returned TRUE but it did NOT save anything. Shouldn't it have returned FALSE?

The Ctor made a call to the DB, the fetch failed, it didn't set any fields (so also not the PK) and then you saved the entity object, which was not there (nothing was set) so the save was a no-op. It didn't return false as there wasn't an error. That's how the method works. If it would have returned false, your code which anticipates on false would go into error check mode which would have been false. Remember, an 'entity' is the DATA inside the entity object, not the entity object itself. So if there's nothing to save, there's 'no entity' there (in the case of new) or nothing was there to sync with the real instance (an existing entity fetched from the db).

Otis wrote:

The new CTor fetching is a convenience route for selfservicing, for people who like to use 'new' instead of factory methods. A different route exists though: CustomerEntity c = new CustomerEntity(); bool fetchResult = c.FetchUsingPK("PKValue");

if the call indeed resulted in a fetch of an entity, fetchResult is true, otherwise it's false. If it's false, the entity isn't in a Fetched state, but still 'new'. FetchUsingPK also has overloads for prefetch paths etc.

It's a little bit more code over the CTor route (not much) so it's up to you what you'd pick. Do you consider this less confusing?

I personally think it would make more sense if FetchUsingPK was static, returned the object if found and returned null if no object was found.

Personally I find that an odd way of doing things in a 'selfservicing' environment, as it then begs the question why is that static method on every entity class instead of on a central class and not generic (as in adapter wink ). Selfservicing means: the entity helps itself. A static (== factory) method isn't about helping itself. Hence the current setup. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
happyfirst
User
Posts: 215
Joined: 28-Nov-2008
# Posted on: 30-Apr-2009 15:51:21   

I understand the issue now. I didn't read the documentation. I still think this aspect of the self servicing design is 'odd', especially since to be understood it requires documentation to be read.

Just out of curiousity, what's an example of how Save would ever return false?

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 30-Apr-2009 21:35:55   

Discussed in this thread

Matt