.IsNull vs. .IsChanged disparity?

Posts   
 
    
Posts: 65
Joined: 07-Dec-2005
# Posted on: 27-Dec-2005 16:32:54   

I'm dealing with an entity collection that was retrieved at a point in time from the database. A particular entity field was null when originally retrieved from the database, so when first accessed, IsChanged is false, and IsNull is true. Hunky-dory, right? Right. So I change the field. Now IsChanged is true, so IsNull doesn't give me what I need (since I care about the current value), so I can check CurrentValue==null. Again, that's fine. However, problems come in when I save the entity. After having saved it with a value in the field (and the value did make it to the database), my IsChanged goes back to false (as I'd expect, since I'm now in sync with the database), but IsNull is still true. I would expect it to be false, since you now know the value in the database (at the time of IsChanged becoming false) is no longer null. It seems to me that these two related properties should stay synced up, but I might be missing something. The same goes for IsNull reporting false even if the value I just persisted is null.

Anyone care to shed some light on this, and hopefully offer a workaround?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 27-Dec-2005 17:26:01   

use: entity.TestCurrentFieldValueForNull(fieldIndex) to test a field if it's null in the in-memory entity.

to test if the field originally was null, (so read as null from the db) use: entity.TestOriginalFieldValueForNull(fieldindex)

Frans Bouma | Lead developer LLBLGen Pro
Posts: 65
Joined: 07-Dec-2005
# Posted on: 27-Dec-2005 17:32:05   

In my context, I have to reference the entity as a generic EntityBase. Because of that, I cannot access the protected versions that take an int fieldIndex rather than the enum from the generated code. Given that, I duplicated the functionality in the protected method in my code (since it's pretty simple). The code I'm using is, I believe, actually the same as the code in CheckIfCurrentFieldValueIsNull in EntityBase. The problem is that if an entity is persisted to the database, IsChanged is reset to false (as it should be), but IsNull is not updated to reflect if the value is null. Even if IsNull shouldn't be updated to reflect what the last persisted or retrieved state was (though I fail to see any actual value in that), the code in your function is using that property, so I think I would end up arriving at the same spot, even if I could get at the protected function.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 27-Dec-2005 18:49:42   

IsNull is used for determining if the original field value is null, as the code is in the non-new branch. That's the sole purpose of IsNull: after a fetch, IsNull is true if the value read from the db (!) was NULL, otherwise it's false.

After a save, if the entity isn't refetched, the flags don't mean anything. In selfservicing, refetching is done automatically when an entity property is read. In adapter, you have to order the code to refetch after a save.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 65
Joined: 07-Dec-2005
# Posted on: 27-Dec-2005 19:48:49   

I'm using SelfServicing, yet I am seeing this behavior. IsChanged is being updated, IsNull is not. What purpose does this flag serve if not to represent that data present at the last database contact? I can certainly understand having it represent the last data read from the database, but if you explicitly save a new value to the database after that, it seems to me that the data should represent that change in the database.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 27-Dec-2005 20:34:45   

AdamRobinson wrote:

I'm using SelfServicing, yet I am seeing this behavior. IsChanged is being updated, IsNull is not. What purpose does this flag serve if not to represent that data present at the last database contact? I can certainly understand having it represent the last data read from the database, but if you explicitly save a new value to the database after that, it seems to me that the data should represent that change in the database.

It's purpose was to signal if the field was null originally. This was required because the DbValue wasn't present in older versions of the runtime lib. It's kept for backwards compatibility.

The flag is set again after the entity is read again. If you save an entity and not fetch it back, teh entity is Outofsync, and by definition in an undefined state.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 65
Joined: 07-Dec-2005
# Posted on: 27-Dec-2005 20:44:42   

OK, here's what I've done, let me know if you have a better suggestion.

For a little background, my binding is done through a custom PropertyDescriptor that, in GetValue(), does checking to determine if the field it represents is null. It's very similar to your CheckCurrentFieldValueIsNull function. it looks something like this;

if(entity.IsNew && !entity.Fields[fieldIndex].IsChanged) treat as null; else if(entity.Fields[fieldIndex].IsChanged && entity.Fields[fieldIndex].CurrentValue==null) treat as null; else if(!entity.Fields[fieldIndex].IsChanged && entity.Fields[fieldIndex].IsNull) treat as null;

You can see where the problem comes from...since the field hasn't changed, IsChanged is false, and since it wasn't refetched (since I didn't actually access a property), IsNull is also true if the first fetch (not the saved value) was null. However, since EntityBase's CheckForRefetch() method is protected, I exposed an internal method called InternalCheckForRefetch() on my custom EntityBase that my base entity classes inherit from, which calls your CheckForRefetch().

Does this sound reasonable?

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 28-Dec-2005 07:06:01   

I guess you missed something in your logic:

if(entity.IsNew && !entity.Fields[fieldIndex].IsChanged)
treat as null;
else if(entity.Fields[fieldIndex].IsChanged && entity.Fields[fieldIndex].CurrentValue==null)
treat as null;
else if(!entity.Fields[fieldIndex].IsChanged && entity.Fields[fieldIndex].IsNull)
treat as null;

should be

if(entity.IsNew && !entity.Fields[fieldIndex].IsChanged)
treat as null;
else if(entity.Fields[fieldIndex].IsChanged && entity.Fields[fieldIndex].CurrentValue==null)
treat as null;
else if(!entity.IsNew && !entity.Fields[fieldIndex].IsChanged && entity.Fields[fieldIndex].IsNull)
treat as null;

note the !entity.IsNew in the last else if statement

Posts: 65
Joined: 07-Dec-2005
# Posted on: 28-Dec-2005 12:53:46   

Thanks, Walla. However, if you'll notice the first code block, the condition of the entity being .IsNew and the field !.IsChanged is already taken care of, and the secondary if statements will only proceed into the other code blocks if this statement fails. If the entity !.IsChanged, which is what the last block checks for, then it must logically be !.IsNew, so there is no need to check for that.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 28-Dec-2005 14:24:35   

Right you are