Link Search Menu Expand Document

ITrace


This interface is used to mark a class to be a trace object. This interface provides all the BEFORE and AFTER operation events.

Methods

Below are the BEFORE methods available from this interface.

  • BeforeAverage - triggered before the Average method is being executed.
  • BeforeAverageAll - triggered before the AverageAll method is being executed.
  • BeforeBatchQuery - triggered before the BatchQuery method is being executed.
  • BeforeCount - triggered before the Count method is being executed.
  • BeforeCountAll - triggered before the CountAll method is being executed.
  • BeforeDelete - triggered before the Delete method is being executed.
  • BeforeDeleteAll - triggered before the DeleteAll method is being executed.
  • BeforeExists - triggered before the Exists method is being executed.
  • BeforeExecuteNonQuery - triggered before the ExecuteNonQuery method is being executed.
  • BeforeExecuteQuery - triggered before the ExecuteQuery method is being executed.
  • BeforeExecuteReader - triggered before the ExecuteReader method is being executed.
  • BeforeExecuteScalar - triggered before the ExecuteScalar method is being executed.
  • BeforeInsert - triggered before the Insert method is being executed.
  • BeforeInsertAll - triggered before the InsertAll method is being executed.
  • BeforeMax - triggered before the Max method is being executed.
  • BeforeMaxAll - triggered before the MaxAll method is being executed.
  • BeforeMerge - triggered before the Merge method is being executed.
  • BeforeMergeAll - triggered before the MergeAll method is being executed.
  • BeforeMin - triggered before the Min method is being executed.
  • BeforeMinAll - triggered before the MinAll method is being executed.
  • BeforeQuery - triggered before the Query method is being executed.
  • BeforeQueryAll - triggered before the QueryAll method is being executed.
  • BeforeQueryMultiple - triggered before the QueryMultiple method is being executed.
  • BeforeSum - triggered before the Sum method is being executed.
  • BeforeSumAll - triggered before the SumAll method is being executed.
  • BeforeTruncate - triggered before the Truncate method is being executed.
  • BeforeUpdate - triggered before the Update method is being executed.
  • BeforeUpdateAll - triggered before the UpdateAll method is being executed.

Below are the AFTER methods available from this interface.

  • AfterAverage - triggered after the Average method has been executed.
  • AfterAverageAll - triggered after the AverageAll method has been executed.
  • AfterBatchQuery - triggered after the BatchQuery method has been executed.
  • AfterCount - triggered after the Count method has been executed.
  • AfterCountAll - triggered after the CountAll method has been executed.
  • AfterDelete - triggered after the Delete method has been executed.
  • AfterDeleteAll - triggered after the DeleteAll method has been executed.
  • AfterExists - triggered after the Exists method has been executed.
  • AfterExecuteNonQuery - triggered after the ExecuteNonQuery method has been executed.
  • AfterExecuteQuery - triggered after the ExecuteQuery method has been executed.
  • AfterExecuteReader - triggered after the ExecuteReader method has been executed.
  • AfterExecuteScalar - triggered after the ExecuteScalar method has been executed.
  • AfterInsert - triggered after the Insert method has been executed.
  • AfterInsertAll - triggered after the InsertAll method has been executed.
  • AfterMax - triggered after the Max method has been executed.
  • AfterMaxAll - triggered after the MaxAll method has been executed.
  • AfterMerge - triggered after the Merge method has been executed.
  • AfterMergeAll - triggered after the MergeAll method has been executed.
  • AfterMin - triggered after the Min method has been executed.
  • AfterMinAll - triggered after the MinAll method has been executed.
  • AfterQuery - triggered after the Query method has been executed.
  • AfterQueryAll - triggered after the QueryAll method has been executed.
  • AfterQueryMultiple - triggered after the QueryMultiple method has been executed.
  • AfterSum - triggered after the Sum method has been executed.
  • AfterSumAll - triggered after the SumAll method has been executed.
  • AfterTruncate - triggered after the Truncate method has been executed.
  • AfterUpdate - triggered after the Update method has been executed.
  • AfterUpdateAll - triggered after the UpdateAll method has been executed.

Use-Cases

If you woud like to see and trace the following.

  • The actual SQL statements composed by the library.
  • The parameter to be used before the execution.
  • The actual result of the execution.
  • The elapsed execution time.

Also, you can use this class to make an audit and cancel the operation before even the actual execution. Please visit the TraceLog and CancellableTraceLog classes to see more information about the trace logging.

How to Implement?

You have to manually create a class that implements this interface.

public class MyCustomTrace : ITrace
{
    public void AfterQuery(TraceLog log)
    {
        // Some implementations here
    }

    public void BeforeQuery(CancellableTraceLog log)
    {
        // Some implementations here
    }

    ...
}

With this, you can log the statement like below.

public class MyCustomTrace : ITrace
{
    public void AfterQuery(TraceLog log)
    {
        Console.WriteLine($"After Query: {log.Statement}");
    }

    public void BeforeQuery(CancellableTraceLog log)
    {
        Console.WriteLine($"Before Query: {log.Statement}");
    }

    ...
}

Or you can even audit and cancel the operation like below.

public class MyCustomTrace : ITrace
{
    public void BeforeQuery(CancellableTraceLog log)
    {
        var invalidActions = new [] { "INSERT", "UDPATE", "DELETE", "DROP", "ALTER", "EXECUTE" };
        if (invalidActions.Any(action => log.Statement.ToUpper().Indexof(action) >= 0))
        {
            Console.WriteLine($"Before Query: A suspicious statement has been passed (SQL = {log.Statement}).");
            log.Cancel(true);
        }
    }

    ...
}

All the fluent methods will be having the BEFORE and AFTER methods within this interface.

Usability

First, as a recommendation, create a factory class that returns the trace.

public static class TraceFactory
{
    private static object _syncLock = new object();
    private static ITrace _trace = null;
    
    public static ITrace CreateTracer()
    {
        if (_trace == null)
        {
            lock (_syncLock)
            {
                if (_trace == null)
                {
                    _trace = new MyCustomTrace();
                }
            }
        }
        return _trace;
    }
}

Then, you can pass it in any extended fluent methods of the DbConnection object like below.

using (var connection = new SqlConnection(connectionString))
{
        var person = connection.Query<Person>(p => p.Id == 10045, trace: TraceFactory.CreateTracer()).FirstOrDefault();
}

Or by passing it on the constructor of the BaseRepository object.

public class PersonRepository : BaseRepository<Person, SqlConnection>
{
    public PersonRepository(ISettings settings)
        : base(settings.Value.ConnectionString, TraceFactory.CreateTracer())
    { }
}

using (var repository = new PersonRepository(new AppSettings()))
{
    var person = repository.Query(p => p.Id == 10045).FirstOrDefault();
}

Or even to the constructor of DbRepository object.

public class DatabaseRepository : DbRepository<SqlConnection>
{
    public DatabaseRepository(ISettings settings)
        : base(settings.Value.ConnectionString, TraceFactory.CreateTracer())
    { }
}

using (var repository = new DatabaseRepository(new AppSettings()))
{
    var person = repository.Query<Person>(p => p.Id == 10045).FirstOrDefault();
}

Always consider to only create a single trace object within the application and have it passed as a singleton object in any operations or reporitories. This is the reason why we had created the factory class above.