Tracing
Hooks before and after every execution for logging, auditing, or cancelling a command outright.
What it is
An ITrace implementation gets BeforeExecution() called right before a command runs and AfterExecution() right after, each keyed by operation name by default so a logger can tell a traced Insert apart from a traced Query.
Writing a trace class
public class NorthwindTrace : ITrace
{
public void BeforeExecution(CancellableTraceLog log) { }
public void AfterExecution<TResult>(ResultTraceLog<TResult> log) =>
logger.Info($"{log.Statement} took {log.ExecutionTime.TotalMilliseconds}ms");
}
connection.Insert<Customer>(customer, trace: new NorthwindTrace());
Injecting it into a repository
Pass the trace instance into BaseRepository or DbRepository’s constructor instead of the connection call, and every operation made through that repository is traced automatically — no trace: argument at each call site.
public class CustomerRepository : BaseRepository<Customer, SqlConnection>
{
public CustomerRepository(IOptions<AppSettings> settings)
: base(settings.Value.ConnectionString, new NorthwindTrace()) { }
}
For dependency injection, define an interface that extends ITrace, register the implementation as a singleton, and accept it as a constructor parameter so the container supplies it.
public interface INorthwindTrace : ITrace { }
public class NorthwindTrace : INorthwindTrace { ... }
services.AddSingleton<INorthwindTrace, NorthwindTrace>();
public class NorthwindRepository : DbRepository<SqlConnection>
{
public NorthwindRepository(IOptions<AppSettings> settings, INorthwindTrace trace)
: base(settings.Value.ConnectionString, trace) { }
}
Cancelling an execution
Because BeforeExecution runs ahead of the actual database call, calling log.Cancel(true) inside it stops the operation before it ever reaches the server — useful for guarding against a statement that doesn’t match what was expected.