Oh my blog! Whee! Wrong by design
  • LINQ code side cascading deletes

    Filed under Programming
    jun 18

    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.

Leave a Reply