Bug? FK key values not updated

Posts   
 
    
philipp
User
Posts: 53
Joined: 15-Feb-2007
# Posted on: 19-Sep-2007 21:01:15   

Hi there

Isn't this a bug? The last line of this test fails which leaves the child group with a reference to its parent group, but with an invalid foreign key value:

//create two groups with IDs 123 / 999
SyncGroup parent = new SyncGroup(123);
SyncGroup child = new SyncGroup(999);

//add child to parent's child collection
parent.ChildGroups.Add(child);

//the parent group ID has been automatically updated
Assert.AreEqual(123, child.ParentGroupId);
Assert.AreSame(parent, child.ParentGroup);

//change parent's ID
parent.GroupId = 1;

//parent group still available?
Assert.AreSame(parent, child.ParentGroup);

//parent reference ID updated?
Assert.AreEqual(parent.GroupId, child.ParentGroupId);

NUnit.Framework.AssertionException: expected: <1> but was: <123>

This is somewhat inconsistent: If I changed the child.ParentGroupId property instead (rather than changing parent.GroupId), LLBLGen would automatically dereference the relation in order to avoid this very situation where the FK value does no longer match the PK of the parent group.

However, this getting worse: If I try to manually correct the foreign key, dereferencing occurs anyway:

child.ParentGroupId = 1;

If I set this property, ParentGroup is dereferenced (set to null) despite the fact that the parent group's ID is infact 1.

Thanks for your feedback Philipp

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 20-Sep-2007 03:52:30   

Hi Philipp, this is copied from LLBLGenPro Help (v2.0) - Using generated code - Adapter - Using the entity classes - FK-PK synchronization:

// C#
OrderEntity myOrder = new OrderEntity();
CustomerEntity myCustomer = new CustomerEntity("CHOPS");
adapter.FetchEntity(myCustomer);
myOrder.Customer = myCustomer;  // A
myOrder.CustomerID = "BLONP";   // B
CustomerEntity referencedCustomer = myOrder.Customer; // C
' VB.NET
Dim myOrder As New OrderEntity()
Dim myCustomer As New CustomerEntity("CHOPS")
adapter.FetchEntity(myCustomer)
myOrder.Customer = myCustomer   ' A
myOrder.CustomerID = "BLONP"    ' B
Dim referencedCustomer As CustomerEntity = myOrder.Customer ' C

After line 'A', myOrder.CustomerID will be set to "CHOPS", because of the synchronization between the PK of Customer and the FK of Order. At line 'B', the foreign key field CustomerID of Order is changed to a new value, "BLONP". Because the FK field changes, the referenced entity through that FK field, Customer, is dereferenced and myOrder.Customer will return null/Nothing. Because there is no current referenced customer entity, the variable referencedCustomer will be set to null / Nothing at line 'C'.

So, the dereference should occurs. What LLBLGen version and what version of Runtime Libraries are you using?

David Elizondo | LLBLGen Support Team
philipp
User
Posts: 53
Joined: 15-Feb-2007
# Posted on: 20-Sep-2007 09:47:29   

Hi Daelmo

I'm not worried about the dereferencing - this is compliant to the documenation and makes sense in order to prevent consistency. I'm having the issue if I change the PK value of the parent group (according to the test I've posted). In this case, the FK is not being updated which leads to the very situation that should be prevented by the dereferencing:

  • change FK: Dereferencing occurs in order to prevent PK/FK mismatch
  • change PK: FK ist NOT updated and NO dereferencing occurs. We have a PK/FK mismatch

Cheers, Philipp

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 20-Sep-2007 12:55:55   

What LLBLGen version and what version of Runtime Libraries are you using?

Please provide the data requested in the above question.

Further thinking about it, I concluded the following:

Assume the following: A parent entity Parent and a child entity Child, where Child.FK = Parent.PK Now if Child and parent are attached to each other (Parent.Child = Child and Child.Parent = Parent)

First use case: Child.FK has changed Either you should dereference them, or Synchronize the PK to match the FK. The later solution is not acceptable or practical. An entity's PK should not change if a referenced entity decided to reference another entity. Needless to say that a PK may in many cases be read-only. Conclusion: A de-reference is required.

Second use case: Parent.PK has changed Either you should dereference them, or Synchronize the FK to match the PK.

Now what would you prefer? That's a puzzling question, but IMHO a cascading update (synchronize the FK to match the PK) is the best option.

Another question is when the synchronization should take place, immediatly or upon saving on the database. By Design this is done once the entities are saved. So if you just called the following before the last Assert, the Assert won't fail.

            DataAccessAdapter adapter = new DataAccessAdapter();
            adapter.SaveEntity(parent, true, true);

Conclusion: Synchronization takes place when the entities get saved.

philipp
User
Posts: 53
Joined: 15-Feb-2007
# Posted on: 20-Sep-2007 14:25:40   

What LLBLGen version and what version of Runtime Libraries are you using?

2.5, August 20, 2007

Walaa wrote:

Second use case: Parent.PK has changed Either you should dereference them, or Synchronize the FK to match the PK.

As I already posted, I can't just synchronize the FK - correcting the FK value would still dereference the child. In order to get the FK to be updated, I would have to

  • remove the childs from their parent's collection
  • re-add them to their parent's collection

This is inconsistent behavior and contrasts the immediate update of the FK if the child is added to the parent (again in the test of the first posting). I'm almost 100% sure this is not how Frans intended it to behave. Look at these 3 cases, the latest one can't be intended like this:

  • if I add a child to the parent, the child's _ParentId _is updated to match the parent's ID
  • if I change the child's ParentId, it's dereferenced because we have a mismatch of FK/PK
  • if I change the parent's _ID, _the child's _ParentId _is **not **updated and we have a mismatch

Cheers, Philipp

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 20-Sep-2007 18:41:49   

It's essential that I know what you're using: selfservicing or adapter, because it makes all the difference in your first codesnippet.

Frans Bouma | Lead developer LLBLGen Pro
philipp
User
Posts: 53
Joined: 15-Feb-2007
# Posted on: 20-Sep-2007 19:54:36   

Hi Frans

Otis wrote:

It's essential that I know what you're using: selfservicing or adapter, because it makes all the difference in your first codesnippet.

I wouldn't have thought this makes a difference simple_smile . I'm using adapter.

Cheers, Philipp

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 21-Sep-2007 11:21:56   

Ok, adapter, so this means the entities are new and not loaded from the DB when you're creating them, nor are the collections fetched from the db simple_smile

philipp wrote:

Hi there

Isn't this a bug? The last line of this test fails which leaves the child group with a reference to its parent group, but with an invalid foreign key value:

//create two groups with IDs 123 / 999
SyncGroup parent = new SyncGroup(123);
SyncGroup child = new SyncGroup(999);

//add child to parent's child collection
parent.ChildGroups.Add(child);

//the parent group ID has been automatically updated
Assert.AreEqual(123, child.ParentGroupId);
Assert.AreSame(parent, child.ParentGroup);

//change parent's ID
parent.GroupId = 1;

//parent group still available?
Assert.AreSame(parent, child.ParentGroup);

//parent reference ID updated?
Assert.AreEqual(parent.GroupId, child.ParentGroupId);

NUnit.Framework.AssertionException: expected: <1> but was: <123>

This is somewhat inconsistent: If I changed the child.ParentGroupId property instead (rather than changing parent.GroupId), LLBLGen would automatically dereference the relation in order to avoid this very situation where the FK value does no longer match the PK of the parent group.

Changing PK's is a thing we consider a bad practise: changing the PK is actually the same as removing the entity and adding a new one with the new PK.

FK fields are synced at two occasions: 1) when an entity reference is made with the PK side, and the PK side has a non-identity PK with a value 2) when the PK side is saved. it then notifies its FK sides that it has been saved and the FK sides will then sync their FK fields prior to getting saved themselves.

However, this getting worse: If I try to manually correct the foreign key, dereferencing occurs anyway:

child.ParentGroupId = 1;

If I set this property, ParentGroup is dereferenced (set to null) despite the fact that the parent group's ID is infact 1.

Correct. To notify that the PK is changed in the PK side entity, an event has to be created and bound by the FK side. This isn't there, so nothing gets notified. If this gets added, the following behavior would be the case: 1) PK value of pk side gets changed 2) FK sides get notified 3) FK sides dereference the PK side, because the PK side is a different entity.

Make no mistake: an entity which gets a different PK is a different entity. That's why it's a bad practise to change the PK, as the PK is the only identifying element for the entity instance (== the data).

I must say that I find this an edge case, because it only occurs when you change the PK value of an entity in a graph. I don't think it is worth the overhead coming with the events.

Frans Bouma | Lead developer LLBLGen Pro
philipp
User
Posts: 53
Joined: 15-Feb-2007
# Posted on: 21-Sep-2007 14:56:00   

Hi Frans

Thanks for the comprehensive reply.

Otis wrote:

Changing PK's is a thing we consider a bad practise: changing the PK is actually the same as removing the entity and adding a new one with the new PK.

I don't fully agree. While I sure like my DMBS to handle all my identities, it's infact a customer requirement I am facing quite frequently. While it does not make sense when it comes to just storing you average customer/order/product data, there are quite a few occasions where the ID infact does have a meaning. You could argue that you could create an DBMS-maintained ID plus another key value with a unique constraint, but this sometimes just doesn't make sense.

Furthermore, I don't regard an item with a new ID a new item. It's just an item with a changed ID. This is supported by every DBMS (by updating foreign keys in order to prevent referential integrity) and IMO both ADO.NET and LLBLGen as well.

Make no mistake: an entity which gets a different PK is a different entity. That's why it's a bad practise to change the PK, as the PK is the only identifying element for the entity instance (== the data).

Which is why the original PK may be stored in order to enable exactly that kind of updates simple_smile . Regarding events: If the parent's PK changes, couldn't this be solved much easier: The parent does have a reference to all it's childs, so the update would not have to be propagated via events. The parent could just change the FKs on it's own with as good as no overhead, couldn't it?

However, with the current situation, what's your recommended pattern for this use case: - user creates groups and child items - user decides to change the group's ID

Keeping the IDs out of sync until a save occurs may create quite a few issues (e.g. if the user performs an export which involves XML serialization that would create markup with the old FK).

Thanks again Philipp

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 21-Sep-2007 18:01:57   

granted this all comes done to symantics and definations, but here's my $.02 on the subject.

philipp wrote:

I don't fully agree. While I sure like my DMBS to handle all my identities, it's infact a customer requirement I am facing quite frequently. While it does not make sense when it comes to just storing you average customer/order/product data, there are quite a few occasions where the ID infact does have a meaning. You could argue that you could create an DBMS-maintained ID plus another key value with a unique constraint, but this sometimes just doesn't make sense.

I would never trust an end user to maintain PKs. it's the db's job to maintain relationships and enforce constraints. if the end user wants to control this, give them the elusion of control with a UserDefinedId and UC.

philipp wrote:

Furthermore, I don't regard an item with a new ID a new item. It's just an item with a changed ID. This is supported by every DBMS (by updating foreign keys in order to prevent referential integrity).

this only works because you have inherent knowledge the ID changed, the db has no way of knowing (maybe parsing through transaction logs, but that's not the purpose of the log.) You would need a history table to track previous/current PK values for the system to know the id changed.

I would argue that RDBMS includes the cascade update feature for novice DBAs/developers or because space was a premium back in the day. The feature has be grandfathered into newer releases because it's required for legacy upgrade paths.

ultimately end users want information, and they want it now. they don't care where it's stored or how it's accesses. as long as they get what they want, when they want, the system works. Therefore they need no say in how the BLL, DAL or DB work.

philipp
User
Posts: 53
Joined: 15-Feb-2007
# Posted on: 21-Sep-2007 21:11:30   

I generally agree, but it's just not that simple. I'm not talking about a centralized (server-side) DB but a local file which is used and shared among users. The DB reflects data of a primary system, and IDs need to be equal. I could of course solve that issue using a "fake ID" with a UC, but the IDs are used by several applications that work with DB and/or primary system, and the ID needs to be used to resolve relations - this would cause a huge performance impact in the "fake ID" is not the PK. I would love to have the DB do all the work, but it's just not an option right now.

Cheers simple_smile