Kategorier
Programming

LINQ code side cascading deletes

Over the last week or so, I’ve been searching around the net for a solution to a problem at work. The problem being that I have a bunch of LINQed objects with parent-child relations between them – the classic Orders -> OrderLines problem.

Normally, you’d create a relation with CASCADE DELETE set on it, but for these objects, I had to do more than just delete the relevant records – for example, I could have a file on disk related to each OrderLine. Deleting the OrderLine should delete the file as well – even if it was a cascaded delete.

You could implement the partial methods DeleteOrder and DeleteOrderLine on the Context class, and delete the file from there. That works for direct deletes, but not for the cascade case, since that only takes place on the sql-server. Also, you cannot call DeleteOnSubmit during SubmitChanges, which is where the partial methods are called.

All I got from google was a bunch of notes on using DeleteOnNull, which doesn’t help me any.

Finally, it dawned on me: You can override SubmitChanges on the Context class. Here’s how I did it:

Public Overrides Sub SubmitChanges(ByVal failureMode As System.Data.Linq.ConflictMode)
  Dim c As Integer = 0
    Dim cs As Changeset = Me.GetChangeSet()

    ' Use a While loop because the count can (will) increase
    While c > cs.Deletes.Count
        Dim deleted As IDeleteEventHandler = TryCast(cs.Deleted(c), IDeleteEventHandler)
        If deleted IsNot Nothing Then
            ' Notify the object that it is about to be deleted.
            deleted.OnDeleting(Me)
        End If
    End While

    MyBase.SubmitChanges(failureMode)

    ' Here I can use a For Each loop because it's
    ' too late to add to the collection anyhow.
    For Each deleted As IDeleteEventHandler In cs.Deleted
        ' Notify the object that it has been deleted.
        deleted.OnDeleted()
    Next
End Sub

And on each object that had to cascade deletes code side, I’d implement the IDeleteEventHandler interface (that I defined to have two methods – OnDelete and OnDeleted), like so:

Partial Public Class Order
    Implements IDeleteEventHandler

    Public Sub OnDeleting(ByVal context As Context) Implements IDeleteEventHandler.OnDeleting
        ' Also delete OrderLines
        context.OrderLines.DeleteAllOnSubmit(Me.OrderLines)
    End Sub 

    Public Sub OnDeleted() Implements IDeleteEventHandler.OnDeleted
        ' No context passed to this method because you shouldn't
        ' do stuff with the context here.

        ' Delete order file from disk
        System.IO.File.Delete(Me.FileName)
    End Sub
End Class

Tah daaaah! There you have it, code side cascading deletes in LINQ.