Does DevForce support setting up Table Splitting in our Entity Framework model? Most things seem to work find with Table Splitting, but I've noticed a few odd behaviors that I think are bugs.
1. Referential Constraint seems to be reversed.
I followed the example of implementing table splitting on
this page (linked to from this
DRC page) which has you setup the Referential Contraint with the 'Principal' entity being the 'parent' and the 'Dependent' one being the child. For example, I took a Dev Force sample project and split up the Customer entity. I moved the address-related fields (Address, City, Region, PostalCode and Country) to a seperate CustomerAddress entity. It looks like this in the designer:
To me, it seems like I should have the Customer entity be the principal and the CustomerAddress be the dependent. For example, this is how I expected to setup the Referential Constraint:

But doing it that way seems to confuse DevForce. For example, with the configuration above, the following code will break:
var em = new NorthwindIBEntities(); //Make a customer var customer = em.CreateEntity<Customer>(); customer.EntityAspect.AddToManager();
//Generate a new Guid as the Id customer.CustomerID = Guid.NewGuid();
//Sanity check DebugFns.Assert(customer.CustomerID != Guid.Empty);
//Make an address var customerAddress = em.CreateEntity<CustomerAddress>(); customerAddress.EntityAspect.AddToManager();
//Sanity check - the ID is still set DebugFns.Assert(customer.CustomerID != Guid.Empty); //Assign the address customer.CustomerAddress = customerAddress;
//Fails! The 'blank' Id from the CustomerAddress entity seems to have overwritten the 'real' ID on the Customer DebugFns.Assert(customer.CustomerID != Guid.Empty); |
I would have expected the Id that I set on the Customer to 'transfer' over to the CustomerAddress entity when I assigned the navigation property (which is the behavior I see in all other cases with DevForce which makes me think its a bug).
I can get around that problem by setting the CustomerID on the customerAddress entity before I assign the Navigation Property but that gets annoying (especially since in my real app, the code isn't nearly as simple as this....there are multipart keys and logic is spread all around).
Anyway, so I added some hacks to do manual copying of PK values before I assign the Navigation Property but then I ran into problems with deletes. This code also seems like it should work but doesn't:
//Make a customervar customer = em.CreateEntity<Customer>(); customer.EntityAspect.AddToManager();
//Generate a new Guid as the Id customer.CustomerID = Guid.NewGuid();
//Sanity check DebugFns.Assert(customer.CustomerID != Guid.Empty);
//Make an address var customerAddress = em.CreateEntity<CustomerAddress>(); customerAddress.EntityAspect.AddToManager();
//Sanity check - the ID is still set DebugFns.Assert(customer.CustomerID != Guid.Empty);
//HACK: Copy over the ID manually customerAddress.CustomerID = customer.CustomerID; //Assign the address customer.CustomerAddress = customerAddress;
//Passes now DebugFns.Assert(customer.CustomerID != Guid.Empty);
//Delete the customer customer.EntityAspect.Delete();
//Both entities should now be detached (since they never got saved, they become detached instead of 'Deleted') DebugFns.Assert(customer.EntityAspect.EntityState == EntityState.Detached); //Fails: The CustomerAddress is still in an Added state DebugFns.Assert(customerAddress.EntityAspect.EntityState == EntityState.Detached); |
After running into those two problems, I thought that maybe DevForce just doesn't support table splitting. But then I saw that your documentation mentions it so I took a second look. I noticed that if I switch around the order of the Referential Constraint so that CustomerAddress is the principal and Customer is the dependent one, all the test code above works as expected (no hacks necessary)!
I started down the road of just reversing the principal/dependent entities hoping that would solve all my problems - even though it felt very wrong and unintuitive....but then I ran into problems because of that. Now that Customer wasn't the principal, DevForce won't let me generate a temporary ID for it. For example, if I try to do:
manager.GenerateId(myCustomer, Customer.PropertyMetadata.CustomerID) |
it will give me an error saying:
Cannot call GenerateId on 'Customer.CustomerID'. GenerateId cannot be called on ForeignKey properties ( even if they are also part of a PrimaryKey). Call GenerateId instead on the 'source' primary key |
So instead, I'd be forced to call GenerateID on the CustomerAddress.CustomerID property....but that is awkward and sometimes impossible (part of the reason I want to do table splitting is so that the client doesn't always need to have the CustomerAddress entity after all!)
#2 Setting Navigation Properties on a Deleted Entity doesn't fixup the IDs
This one confused me for a while but in the end, it's very easy to reproduce. I'll build on the scenario given above with Customer and CustomerAddress. For this example, I'll have the Referential Constraint "reversed" so that setting Navigation Properties works. The difference in this case is that I delete the parent entity before assigning the navigation property.
//Make a customervar customer = em.CreateEntity<Customer>(); customer.EntityAspect.AddToManager();
//Generate a new Guid as the Id customer.CustomerID = Guid.NewGuid();
//Sanity check DebugFns.Assert(customer.CustomerID != Guid.Empty);
//Make an address var customerAddress = em.CreateEntity<CustomerAddress>(); customerAddress.EntityAspect.AddToManager();
//Need to AcceptChanges before doing the delete otherwise the entity will just get detached customer.EntityAspect.AcceptChanges();
//Delete the customer. customer.EntityAspect.Delete();
//The customer still has its ID even though it is deleted DebugFns.Assert(customer.CustomerID != Guid.Empty);
//Assign the address customer.CustomerAddress = customerAddress;
//Since I reversed the Referential Constraint, the Customer.CustomerID property didn't get cleared DebugFns.Assert(customer.CustomerID != Guid.Empty);
//Fails! The CustomerAddress.CustomerID is still empty....even though it got assigned to the navigation property! DebugFns.Assert(customerAddress.CustomerID != Guid.Empty); |
If I comment out the line that deletes the customer, this all works fine. Also, if I try it on other entities that aren't table-split ones, it all works fine (for example, I tested adding an Order to a deleted Customer and the Order.CustomerID property still got updated correctly).
Hopefully that is all clear. Thanks!