πŸŽ‹ Cascading

back

Cascading means that upon deleting a main entity, child-entities are deleted too.

Contents

Introduction

Entities can be automatically Deleted along with other Entities. But if they are not inherently part of the main Entity, they would be Unlinked instead.

This can be implemented as a pattern in C#. A reason to do it in C#, is to explicitly see in the code, that the other Deletions take place. It may be important not to hide this from view.

One way to implement Cascading, is through extension methods:
DeleteRelatedEntities and UnlinkRelatedEntities.

Code Files

Here is a suggestion for how to organize the Cascading code.

In the csproj of the Business layer, you could put a sub-folder called Cascading and put two code files in it:

JJ.Ordering.Business.csproj
    |
    |- Cascading
        |
        |- DeleteRelatedEntitiesExtensions.cs
        |- UnlinkRelatedEntitiesExtensions.cs

DeleteRelatedEntities

Here is how DeleteRelatedEntitiesExtensions.cs might look internally:

/// <summary>
/// Deletes child entities inherently part of the main entity.
/// </summary>
public static class DeleteRelatedEntitiesExtensions
{
    public static void DeleteRelatedEntities(this Order order)
    {
        ...
    }
}

In there, child Entities are successively Deleted:

public static class DeleteRelatedEntitiesExtensions
{
    public static void DeleteRelatedEntities(this Order order)
    {
        // Delete child entities.
        foreach (var orderLine in order.OrderLines.ToArray())
        {
            _repository.Delete(orderLine);
        }
    }

(Note: The ToArray can prevent an Exception about the loop collection being modified.)

Before an extension method Deletes a child Entity, it might call Cascading upon the child Entity too:

public static void DeleteRelatedEntities(this Order order)
{
    foreach (var orderLine in order.OrderLines.ToArray())
    {
        // Call cascading on the child entity too!
        orderLine.UnlinkRelatedEntities(); 

        _repository.Delete(orderLine);
    }
}

UnlinkRelatedEntities

UnlinkRelatedEntities might be a little bit easier. It neither requires Repositories not does it do much recursion:

/// <summary>
/// Unlinks related entities, not inherently part of the main entity.
/// </summary>
public static class UnlinkRelatedEntitiesExtensions
{
    public static void UnlinkRelatedEntities(this OrderLine orderLine)
    {
        orderLine.UnlinkOrder();
        orderLine.UnlinkProduct();
    }
}

Note that it uses the Unlink pattern discussed earlier.

Delete Main Entity

The Cascading extension methods delete related Entities, not the main Entity. The idea behind that is: Where a main Entity is Deleted, we could call the Cascading methods first:

entity.DeleteRelatedEntities();
entity.UnlinkRelatedEntities();

// Delete main entity separately.
_repository.Delete(entity);

That way we can see explicitly that more Deletions take place.

Cascading & Repositories

The DeleteRelatedEntities methods might need Repositories to perform the Delete operations.

You could pass these Repositories as parameters:

public static void DeleteRelatedEntities(
    this Order order,
    /* Repository parameter */
    IOrderLineRepository repository)
{
    foreach (var orderLine in order.OrderLines.ToArray())
    {
        orderLine.UnlinkRelatedEntities();
        repository.Delete(orderLine);
    }
}

Or you might make repositories available through a technique called dependency injection.

It’s up to you. The choice to use extension methods was also a matter of preference.

Nuance

Sometimes an Entity indeed has related Entities to Cascadedly Unlink or Delete, but sometimes it doesn’t, creating subtleties in the implementation.

Alternative: Database Cascading

Instead of using C# to perform the Cascading you could also configure the database to do it for you. You could use Triggers or Delete Actions for that.

But this might not play along nicely with our data access technology of choice: ORM. Saving the changes could then complain about too many records modified, because more records were affected than the ORM expected.

Alternative: ORM-Mappings

You can also configure the ORM through Mappings to automatically handle the Cascading.

A downside of this might be, that the deletions are hidden away from view. It may surprise programmers, when related data is automatically deleted. This can result in unintended consequences, taking away control from the programmer.

Conclusion

Hopefully this gave a good impression of how you could build up Cascading code by just using a pattern in C#.

back