Introduction
If you are using the EF6 and want to log the database operations, analyze them, then, this is the right place for you.
Background
When I was developing one WebApi project, what I was searching for is an output each query performed by Entity framework should be logged with time. Also, exception if there is any. So, in this section, you will learn how to log commands and queries to the database generated by Entity framework.
Using the Code
There are two ways:
1) Simple Method
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
}
This will log the database operations in the output window. What it does is it writes the operations performed by EntityFramework
to the output window. It gives awesome traces.
Have a look:

2) IDbCommandInterceptor
This uses the IDbCommandInterceptor
Interface. This is in-built in Entity framework 6
.
Note: This Interface
is available only in Entityframework 6 and later.
Have a look at Interface
:
namespace System.Data.Entity.Infrastructure.Interception
{
public interface IDbCommandInterceptor : IDbInterceptor
{
void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext);
void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext);
void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext);
void ReaderExecuted(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext);
void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext);
void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext);
}
}
Let’s derive this interface to the DatabaseLogger
class.
FYI, I have added my log information into database. You may insert into file, Excel, anything you want.
You need not worry about the methods Interface
itself is very self explanatory. It has 6 methods.
You can see that I have derived
and check comments to understand each methods
.
public class DatabaseLogger : IDbCommandInterceptor
{
static readonly ConcurrentDictionary<DbCommand,
DateTime> MStartTime = new ConcurrentDictionary<DbCommand, DateTime>();
public void NonQueryExecuted(DbCommand command,
DbCommandInterceptionContext<int> interceptionContext)
{
Log(command, interceptionContext);
}
public void NonQueryExecuting(DbCommand command,
DbCommandInterceptionContext<int> interceptionContext)
{
OnStart(command);
}
public void ReaderExecuted(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
Log(command,interceptionContext);
}
public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
OnStart(command);
}
private static void Log<T>(DbCommand command,
DbCommandInterceptionContext<T> interceptionContext)
{
DateTime startTime;
TimeSpan duration;
MStartTime.TryRemove(command, out startTime);
if (startTime != default(DateTime))
{
duration = DateTime.Now - startTime;
}
else
duration = TimeSpan.Zero;
const int requestId = -1;
var parameters = new StringBuilder();
foreach (DbParameter param in command.Parameters)
{
parameters.AppendLine(param.ParameterName + " " +
param.DbType + " = " + param.Value);
}
var message = interceptionContext.Exception == null ?
$"Database call took {duration.TotalSeconds.ToString("N3")} sec.
RequestId {requestId} \r\nCommand:\r\n{parameters + command.CommandText}" :
$"EF Database call failed after {duration.TotalSeconds.ToString("N3")} sec.
RequestId {requestId} \r\nCommand:\r\n{parameters.ToString() +
command.CommandText}\r\nError:{interceptionContext.Exception} ";
if (duration.TotalSeconds>1 || message.Contains("EF Database call failed after "))
{
using (DbContext dbContext = new DbContext())
{
Error error = new Error
{
TotalSeconds = (decimal)duration.TotalSeconds,
Active = true,
CommandType = Convert.ToString(command.CommandType),
CreateDate = DateTime.Now,
Exception = Convert.ToString(interceptionContext.Exception),
FileName = "",
InnerException = interceptionContext.Exception == null ?
"" : Convert.ToString(interceptionContext.Exception.InnerException),
Parameters = parameters.ToString(),
Query = command.CommandText,
RequestId = 0
};
dbContext.Errors.Add(error);
dbContext.SaveChanges();
}
}
}
public void ScalarExecuted
(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
Log(command, interceptionContext);
}
public void ScalarExecuting
(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
OnStart(command);
}
private static void OnStart(DbCommand command)
{
MStartTime.TryAdd(command, DateTime.Now);
}
}
Now, I am registering this class to dbcontext
.
Explanation: It tells entity framework to use this class for Logging
database operations.
public DbContext(): base("name=connectionstring")
{
DbInterception.Add(new DatabaseLogger());
}
Everything is set up now. You can make an Error
model class like this. (This is the code-first model class.)
public class Error
{
[Key]
[Required]
public int ErrorId { get; set; }
public string Query { get; set; }
public string Parameters { get; set; }
public string CommandType { get; set; }
public decimal TotalSeconds { get; set; }
public string Exception { get; set; }
public string InnerException { get; set; }
public int RequestId { get; set; }
public string FileName { get; set; }
public DateTime CreateDate { get; set; }
public bool Active { get; set; }
}
Have a look at Error logs here:

Technically, there are many approaches to log your Database operations. This is the easiest way I saw.
Credits
Bonus
I just used this so thought about sharing it here too
Let's have a quick look at logging the time taken by each controller. Same table's structure, same queries.
- Add a new class named
ExecutionTimeFilter
like I have added:
public class ExecutionTimeFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
actionContext.Request.Properties.Add("Time", Stopwatch.StartNew());
}
public override void OnActionExecuted(HttpActionExecutedContext actionContext)
{
base.OnActionExecuted(actionContext);
try
{
var stopwatch = (Stopwatch)actionContext.Request.Properties["Time"];
actionContext.Request.Properties.Remove("Time");
var elapsedTime = stopwatch.Elapsed;
if (!(elapsedTime.TotalSeconds > 10)) return;
using (DbContext dbContext = new DbContext())
{
Error error = new Error
{
TotalSeconds = (decimal)elapsedTime.TotalSeconds,
Active = true,
CommandType = "Action Context",
CreateDate = DateTime.Now,
Exception = Convert.ToString(actionContext.Request),
FileName = "",
InnerException = actionContext.Response.ToString(),
Parameters = "",
Query = "",
RequestId = 0
};
dbContext.Errors.Add(error);
dbContext.SaveChanges();
}
}
catch
{
}
}
}
- Register it to the
config
in my case I am using it in web API so in App_Start/WebApiConfig.cs
:
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ExecutionTimeFilter());
}
Done! now, every action executed and took more than 10 seconds will log that entry to the database.