This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
After doing some more testing it is clear to me that the support for cascading deletes is broken in Lightspeed. According to the documentation, I am supposed to be able to control whether cascading deletes are performed on a particular association by using the 'Is Dependent' property of the association. However this property does not work as advertised. The only way to turn off cascading deletes it to turn it off for the entire entity, or in the entire context. The way it works when those values are left at the default settings is dependent on the 'Is Nullable' property of the association, and it will work as follows:
Is Dependent = false When this is the case, Lightspeed will not perform a cascading delete on related entities, but will rather attempt to break the associations for related entities by setting them to null, such as with the following SQL: UPDATE However is clear looking through the code that when the 'Is Dependent' property is set to false, Lightspeed will NOT evict any entities from the identity map that are a part of the cascade (which is the correct behaviour). But it also does not break the associations for any entities loaded in the identity map, even though that is what the SQL code is doing.
Is Dependent = true When this is the case, Lightspeed will perform a cascading delete on related entities as you would expect. It both does this for any entities in the identity map, and it also does it for any entites in the database by producing the folowing SQL: DELETE FROM
Is Dependent = false When this is the case, Lightspeed will STILL perform a cascading delete on related entities which is NOT what you would expect. And it does not evict the entities from the identity map either, even though it issues the SQL to delete the entities from the database the same as in the above case. If you examine the code, all of this fun stuff happens in the DeleteQueryBuilder.cs and InMemoryDeleteCascader.cs in the Framework\Mapping directory. From what I can tell the InMemoryDeleteCascader.cs is written correctly, and it contains the following code: foreach (var toManyModel in typeModel.ToManyModels) this goes through all the toMany models for the entity, and as you can see it is ignores any associations where cascading deletes is turned off for the entity, or where it is turned off for the asscociation (unless the entity is transient and not stored in the database). And the function is never called if cascading deletes is turned off in the entire context. This is the correct behaviour. However if you check the code in DeleteQueryBuilder, it does the following: if (!typeModels[0].TypeModel.CascadeDeletes) As you can see it first bails if cascading deletes is turned off in the entity, and then bails if it is turned off in the unit of work. However when it processes all the associations, it NEVER checks the association itself to see if it is dependent! It checks for transient entities, but not for dependent associations. Hence the code to perform the cascading delete is ALWAYS executed no matter what (unless you turn it off in the entity or in the context). I believe the code should be changed to the following: var toManyModels = typeModels[0].TypeModel.IsClassTableInheritance ? typeModels[0].TypeModel.ToManyModels : typeModels[0].TypeModel.FlattenedToManyModels; This will then work as expected and the dependent property of the association will be correctly honored. However if you do that, I think it means that no matter what, it will never execute the update code from the first case for nullable associations that will try to break the links to related entities. To me that is actually the correct behaviour, because if I set an association to non-dependent, I think it should NEVER try to do anything with related entities. It is not entirely clear to me why Lightspeed is even trying to do that, but perhaps the correct relationship between these properties should be as follows: Is Dependent = false RESULT: Related entities are never touched Is Dependent = true RESULT: Related entities are deleted from the identity MAP and from the database Is Dependent = true RESULT: Related entities are not deleted, but rather the associations are broken Of course that leaves us with the open question of how you would support cascading deletes natively in the database, since you would not want to turn it off in the context because then Lightspeed won't ever try to evict entities in the identity map. It seems to me there should be an option to still do the in memory delete cascade, but not do the physical database cascade in the case where the database will be doing cascading deletes natively (InnoDB for instance). Perhaps that should be a context wide option to control this? |
|
|
It is clear based on how the FieldModel.Cascade propery is implemented that cascade deletes will always occur in some form. It is implemented as follows: public virtual bool Cascade so cascade deletes for an association depend on the foreign key not being nullable OR the association being marked as dependent. So no matter what you do, the 'dependent' association attribute will never turn off cascading deletes at all. It will just turn them on if the association is nullable, and has no effect on an association that is NOT nullable. So the fix I proposed is not valid if this is the intended behaviour. I am not entirely sure why that is the desired behaviour, but if it is, this should be better documented in the tooltips in the designer for the IsDependent association proprerty, and the IsNullable property of the association. As well as the online user guide. I would like the reasons why it is implemented that way to be in the users guide, so it is clear why things will happen the way that they do. And in addition to this, if this IS the desired behaviour, then I am still convinced there is a bug in the in memory delete cascader, because it is not performing the same basic operation. It will only cascade delete entities in memory if the dependent attribute is set if the association is nullable. Unless I missed something, it does not appear to do anything to disconnect entities in memory for all the associations that are set to nullable (but it is possible I missed something as I am not that familiar with the code). |
|