- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Bug? FK key values not updated
Joined: 15-Feb-2007
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
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?
Joined: 15-Feb-2007
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
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.
Joined: 15-Feb-2007
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
Joined: 15-Feb-2007
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 . I'm using adapter.
Cheers, Philipp
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
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.
Joined: 15-Feb-2007
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 . 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
Joined: 05-Jul-2006
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.
Joined: 15-Feb-2007
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