我曾經寫過這麼一篇文章「ASP.NET MVC + NLog + Clutch.Diagnostics.EntityFramework 追蹤 EF 執行的 SQL Command」介紹可以使用 NLog 與 Clutch.Diagnostics.EntityFramework 在偵錯模式下可以將 EF 所產生並執行的 SQL Command 給顯示在 Visual Studio 的 Output 視窗中,如果網站放到測試機或是正式機要查看系統裡 EF 所執行的 SQL Command 還是可以透過 MiniProfiler 或 Glimpse,這兩個工具我之前也介紹過。
Entity Framework 6 提供了一個新功能「Logging」,這個功能可以取代 NLog + Clutch.Diagnostics.EntityFramework,不過這只有 Entity Framework 6 才有提供,使用 EF 5 之前版本則是沒有這個功能,以下就說明怎麼使用這個功能。
參考文件:
這邊繼續使用上一篇「初探 Entity Framework 6 的 Async/Await 功能」所建立的網站來做操作,依照 EF6 教學文件的說明,我們假如要使用 EF6 的 Logging 功能,可用以下的方式,
但我們所執行的是網站而不是 Console 專案,所以還是希望可以將 Log 給輸出到 Visual Studio 的 Output 視窗中,所以可以改成以下的方式,程式裡所使用的 Debug,其命名空間為「System.Diagnostics」,
執行結果
可以看到 EF 所執行的 SQL Command、執行開始時間以及執行所需時間。
上面這個 Log 的內容還可看到執行 SQL Command 的 Parameter 名稱、資料、類別、大小等。
如果 Controller 裡並不是直接對 DbContext 做資料存取而是透過 Repository 呢?我這邊的作法則是在 GenericRepository 的建構式內加上程式碼,這樣就不需要一個一個去手動加入程式碼。
在教學文件上面有看到 Log 的內容格式是可以讓我們自己設定,依照文件的說明,要建立一個繼承 DatabaseLogFormatter 的 OneLineFormatter,
OneLineFormatter 內容
public class OneLineFormatter : DatabaseLogFormatter
{
public OneLineFormatter(DbContext context, Action<string> writeAction)
: base(context, writeAction)
{
}
public override void LogCommand<TResult>(
DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
Write(string.Format(
"Context '{0}' is executing command '{1}'{2}",
Context.GetType().Name,
command.CommandText.Replace(Environment.NewLine, ""),
Environment.NewLine));
}
public override void LogResult<TResult>(
DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
}
}
接著就是要建立一個 MyDbConfiguration.cs 檔案,
MyDbConfiguration 程式內容
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetDatabaseLogFormatter(
(context, writeAction) => new OneLineFormatter(context, writeAction));
}
}
只要建立好 OneLineFormatter 與 MyDbConfiguration 這兩個檔案就可以了,不必再更動其他的程式設定,就可以改變 EF 的 Log 格式,
以上是直接取用 Entity Framework 教學文件裡的程式內容,如果各位真的需要自訂一個 Log Formatter 的話,可以參考 Entity Frameowork 的原始碼,現在 Entity Framework 已經開放原始碼了,所以可以到以下的網址下載,
http://entityframework.codeplex.com
可以參考「DatabaseLogFormatter.cs」這個檔案的內容,尤其是需要好好地看「LogCommand<TResult>, LogParameter<TResult>, LogResult<TResult>」這幾個方法的內容,我這邊就不再另外說明,我是覺得預設的 LogFormatter 就已經可以滿足我查看資料的需求。
/// <summary>
/// Called to log a command that is about to be executed. Override this method to change how the
/// command is logged to <see cref="WriteAction" />.
/// </summary>
/// <typeparam name="TResult">The type of the operation's results.</typeparam>
/// <param name="command">The command to be logged.</param>
/// <param name="interceptionContext">Contextual information associated with the command.</param>
public virtual void LogCommand<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
Check.NotNull(command, "command");
Check.NotNull(interceptionContext, "interceptionContext");
var commandText = command.CommandText ?? "<null>";
if (commandText.EndsWith(Environment.NewLine, StringComparison.Ordinal))
{
Write(commandText);
}
else
{
Write(commandText);
Write(Environment.NewLine);
}
if (command.Parameters != null)
{
foreach (var parameter in command.Parameters.OfType<DbParameter>())
{
LogParameter(command, interceptionContext, parameter);
}
}
Write(interceptionContext.IsAsync
? Strings.CommandLogAsync(DateTimeOffset.Now, Environment.NewLine)
: Strings.CommandLogNonAsync(DateTimeOffset.Now, Environment.NewLine));
}
/// <summary>
/// Called by <see cref="LogCommand" /> to log each parameter. This method can be called from an overridden
/// implementation of <see cref="LogCommand" /> to log parameters, and/or can be overridden to
/// change the way that parameters are logged to <see cref="WriteAction" />.
/// </summary>
/// <typeparam name="TResult">The type of the operation's results.</typeparam>
/// <param name="command">The command being logged.</param>
/// <param name="interceptionContext">Contextual information associated with the command.</param>
/// <param name="parameter">The parameter to log.</param>
public virtual void LogParameter<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext, DbParameter parameter)
{
Check.NotNull(command, "command");
Check.NotNull(interceptionContext, "interceptionContext");
Check.NotNull(parameter, "parameter");
// -- Name: [Value] (Type = {}, Direction = {}, IsNullable = {}, Size = {}, Precision = {} Scale = {})
var builder = new StringBuilder();
builder.Append("-- ")
.Append(parameter.ParameterName)
.Append(": '")
.Append((parameter.Value == null || parameter.Value == DBNull.Value) ? "null" : parameter.Value)
.Append("' (Type = ")
.Append(parameter.DbType);
if (parameter.Direction != ParameterDirection.Input)
{
builder.Append(", Direction = ").Append(parameter.Direction);
}
if (!parameter.IsNullable)
{
builder.Append(", IsNullable = false");
}
if (parameter.Size != 0)
{
builder.Append(", Size = ").Append(parameter.Size);
}
if (((IDbDataParameter)parameter).Precision != 0)
{
builder.Append(", Precision = ").Append(((IDbDataParameter)parameter).Precision);
}
if (((IDbDataParameter)parameter).Scale != 0)
{
builder.Append(", Scale = ").Append(((IDbDataParameter)parameter).Scale);
}
builder.Append(")").Append(Environment.NewLine);
Write(builder.ToString());
}
/// <summary>
/// Called to log the result of executing a command. Override this method to change how results are
/// logged to <see cref="WriteAction" />.
/// </summary>
/// <typeparam name="TResult">The type of the operation's results.</typeparam>
/// <param name="command">The command being logged.</param>
/// <param name="interceptionContext">Contextual information associated with the command.</param>
public virtual void LogResult<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
Check.NotNull(command, "command");
Check.NotNull(interceptionContext, "interceptionContext");
if (interceptionContext.Exception != null)
{
Write(Strings.CommandLogFailed(
Stopwatch.ElapsedMilliseconds, interceptionContext.Exception.Message, Environment.NewLine));
}
else if (interceptionContext.TaskStatus.HasFlag(TaskStatus.Canceled))
{
Write(Strings.CommandLogCanceled(Stopwatch.ElapsedMilliseconds, Environment.NewLine));
}
else
{
var result = interceptionContext.Result;
var resultString = (object)result == null
? "null"
: (result is DbDataReader)
? result.GetType().Name
: result.ToString();
Write(Strings.CommandLogComplete(Stopwatch.ElapsedMilliseconds, resultString, Environment.NewLine));
}
Write(Environment.NewLine);
}
這篇就先講到這裡。
參考連結:
以上
thank you,sir!
回覆刪除you're welcome!
刪除筆記中...
刪除謝謝Kevin大
小章哥客氣了,還需要向你學習呢...
刪除感謝kevin大大分享,又學到一招了。
回覆刪除看見這一篇文章我感動的都要哭了~ 感謝分享
回覆刪除有這麼誇張? >.^
刪除