Last active
September 7, 2016 15:18
-
-
Save restlessmedia/b38caca4149f090403ae520f78c4b829 to your computer and use it in GitHub Desktop.
Entity Framework DbCommandInterception for instrumentation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CommandInterceptionOptions | |
{ | |
public CommandInterceptionOptions() | |
{ | |
WarningThreshold = TimeSpan.FromSeconds(2); | |
} | |
public TimeSpan WarningThreshold { get; set; } | |
public static CommandInterceptionOptions Default = new CommandInterceptionOptions(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class DatabaseConfiguration : DbConfiguration | |
{ | |
public DatabaseConfiguration() | |
{ | |
DbInterception.Add(new DbCommandInterceptor()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class DatabaseContext : DbContext | |
{ | |
static DatabaseContext() | |
{ | |
Database.SetInitializer<DatabaseContext>(null); | |
DbConfiguration.SetConfiguration(new DatabaseConfiguration()); | |
} | |
public DatabaseContext(bool autoDetectChangesEnabled = false) | |
: base("defaultConnection") | |
{ | |
Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled; | |
Configuration.LazyLoadingEnabled = false; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class DbCommandInterceptor : IDbCommandInterceptor | |
{ | |
public DbCommandInterceptor(CommandInterceptionOptions options = null) | |
{ | |
_options = options ?? CommandInterceptionOptions.Default; | |
} | |
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) | |
{ | |
OnExecuted(command, interceptionContext); | |
} | |
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) | |
{ | |
OnExecuted(command, interceptionContext); | |
} | |
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) | |
{ | |
OnExecuted(command, interceptionContext); | |
} | |
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) | |
{ | |
OnExecuting(command); | |
} | |
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) | |
{ | |
OnExecuting(command); | |
} | |
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) | |
{ | |
OnExecuting(command); | |
} | |
private void OnExecuted<T>(DbCommand command, DbCommandInterceptionContext<T> interceptionContext) | |
{ | |
DateTime startTime; | |
TimeSpan duration = _commandCache.TryRemove(command, out startTime) ? DateTime.Now - startTime : TimeSpan.Zero; | |
if (ShouldLog(command, duration, interceptionContext)) | |
LogMessage(GetLogMessage(command, duration, interceptionContext), duration); | |
} | |
private void LogMessage(string message, TimeSpan duration) | |
{ | |
if (IsWithinThreshold(duration)) | |
Trace.TraceInformation(message); | |
else | |
Trace.TraceWarning(message); | |
} | |
private bool IsWithinThreshold(TimeSpan duration) | |
{ | |
return duration.CompareTo(_options.WarningThreshold) < 0; | |
} | |
private bool ShouldLog<T>(DbCommand command, TimeSpan duration, DbCommandInterceptionContext<T> interceptionContext) | |
{ | |
return interceptionContext.Exception != null || !IsWithinThreshold(duration); | |
} | |
private readonly CommandInterceptionOptions _options; | |
private static void OnExecuting(DbCommand command) | |
{ | |
_commandCache.TryAdd(command, DateTime.Now); | |
} | |
private static string GetLogMessage<T>(DbCommand command, TimeSpan duration, DbCommandInterceptionContext<T> interceptionContext) | |
{ | |
if (interceptionContext.Exception == null) | |
return GetLogMessage(command, duration); | |
return GetLogMessage(command, duration, interceptionContext.Exception); | |
} | |
private static string GetLogMessage(DbCommand command, TimeSpan duration, Exception exception) | |
{ | |
const string exceptionMessageFormat = "Database call failed after {0} sec. \r\nCommand:\r\n{1}\r\nError:{2}"; | |
return string.Format(exceptionMessageFormat, FormatDuration(duration), GetCommandText(command), exception); | |
} | |
private static string GetLogMessage(DbCommand command, TimeSpan duration) | |
{ | |
const string messageFormat = "Database call took {0} sec. \r\nCommand:\r\n{1}"; | |
return string.Format(messageFormat, FormatDuration(duration), GetCommandText(command)); | |
} | |
private static string FormatDuration(TimeSpan duration) | |
{ | |
const string format = "N3"; | |
return duration.TotalSeconds.ToString(format); | |
} | |
private static string GetCommandText(DbCommand command) | |
{ | |
if (command.Parameters.Count == 0) | |
return command.CommandText; | |
const string space = " "; | |
const string equals = " = "; | |
StringBuilder parameters = new StringBuilder(); | |
for (int i = 0; i < command.Parameters.Count; i++) | |
{ | |
DbParameter param = command.Parameters[i]; | |
parameters.AppendLine(string.Concat(param.ParameterName, space, param.DbType, equals, param.Value)); | |
} | |
parameters.Append(command.CommandText); | |
return parameters.ToString(); | |
} | |
private static readonly ConcurrentDictionary<DbCommand, DateTime> _commandCache = new ConcurrentDictionary<DbCommand, DateTime>(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample output:
Database call took 0.001 sec.
Command:
select cast(serverproperty('EngineEdition') as int)