LINQ To SQL – Audit changes

I was working on a project where I needed to insert an audit record when changes to the database were made.  My first thought was to use triggers, but unfortunately, the deployment model of this application made that option impossible.  So then I turned to the data access layer of the application.  Sure, an “easy” option would’ve been to go through all the calls to the database and add a call to the “audit” function, but that seemed error-prone and difficult to maintain.  Then i turned to the Data Context, which is handled by the DBML class in Linq to SQL.  The DBML inherits from System.Data.Linq.DataContext.  I created a new base class called “AuditableContext” which inherits from DataConext and used it as an interception point for my calls to the Database.  It was easy to switch the DBML to inherit from my new AuditableContext class…just open up the DBML in design mode and go to its properties, then set the Base Class property.

image

Having done that, now I had a nice interception point for all my calls.  So now I created an Override method for SubmitChanges(), which gets called on any Insert, Update, Delete operation using L2S (note, that this is not called for stored procedures).  In SubmitChanges() I can interrogate the changeset and pull out the fields that changed, the before and after values, and of course the table name.  I also added an “AuditEnabled” property that allows me to turn the behavior on or off (it’s off by default) at the time that i instantiate the context.  Here is the code that gets the changes out…it’s VB.NET, so don’t be afraid 🙂   I simplified the code a bit for the demo, so be sure to check for nulls and apply good exception handling…this shows the logic to capture updates, but if you need inserts and deletes as well, you can follow the same pattern. 

 Public Overrides Sub SubmitChanges(ByVal failureMode As System.Data.Linq.ConflictMode)
        If AuditEnabled Then
            Dim _Updates As IEnumerable = GetChangeSet().Updates
            If Not _Updates Is Nothing Then
                For Each _Entity As Object In _Updates
                    Dim _EntityType As Type = _Entity.GetType()
                    Dim _Table As ITable = GetTable(_EntityType)
                    Dim _ModifiedList As ModifiedMemberInfo() = _Table.GetModifiedMembers(_Entity)

                    For Each _ModifiedData As ModifiedMemberInfo In _ModifiedList
                        Dim _OriginalValue As String = _ModifiedData.OriginalValue.ToString
                        Dim _CurrentValue As String = _ModifiedData.CurrentValue.ToString

                        If m_OriginalValue <> m_CurrentValue Then
                            LogAudit(_EntityType.Name, _ModifiedData.Member.Name, _OriginalValue, _CurrentValue)
                        End If
                    Next
                Next
            End If
        End If

        MyBase.SubmitChanges(failureMode)
    End Sub

About esteban

Esteban is the Founder and Chief Technologist at Nebbia Technology, an ALM consulting and Azure-powered technology company. He is a software developer with a passion for ALM, TFS, Azure, and software development best practices. Esteban is a Microsoft Visual Studio ALM MVP and ALM Ranger, Pluralsight author, and the president of ONETUG (Orlando .NET User Group).

2 thoughts on “Building dynamic where logic in LINQ

  1. Anonymous

    Hi There – Can you let me know what myType refers to?

    Thanx

    marcus.leedham@yaoo.co.uk

  2. Esteban Garcia

    Hello Marcus,
    In this case, myType would be whatever LINQ to SQL type you the predicate is acting on . So if you have a table called Product that you want to apply the dymanic logic to, that line would read:
    Dim predicate = PredicateBuilder.False(Of Product)()

    I hope that helps!


Leave a Reply

Your email address will not be published. Required fields are marked *

Are you human? *