this threadに従って、生成されたSQL
をEF
経由で記録できますが、DbContext.SaveChanges()
はどうでしょうか?余分なフレームワークなしでこの仕事をする簡単な方法はありますか?
エンティティフレームワーク6.0では、DatabaseクラスにAction<string> Log
プロパティがあります。したがって、ロギングの設定は次のように簡単です。
context.Database.Log = Console.WriteLine;
より高度なニーズについては、 interceptor を設定できます。エンティティフレームワークの詳細 wiki
http://www.codeproject.com/Articles/499902/Profiling-Entity-Framework-5-in-code を参照してください。クック氏のアイデアを、Code First、POCO DbContext、Entity Framework 5を使用してasp.net mvcアプリケーションに実装しました。
アプリケーションのコンテキストクラスは、DbContextから派生します。
public class MyDbContext : DbContext
コンテキストのコンストラクターがSavingChangesイベントをフックします(デバッグビルドの高価なリフレクションのみを実行したい)。
public MyDbContext(): base("MyDbContext")
{
#if DEBUG
((IObjectContextAdapter)this).ObjectContext.SavingChanges += new EventHandler(objContext_SavingChanges);
#endif
}
変更の保存イベントは、生成されたSQLを出力ウィンドウに書き込みます。クック氏からコピーしたコードは、DbParameterをSqlParamterに変換します。これは、SQL Serverにアクセスしているため、そのままにしておきますが、他の種類のデータベースにアクセスすると変換が失敗すると想定しています。
public void objContext_SavingChanges(object sender, EventArgs e)
{
var commandText = new StringBuilder();
var conn = sender.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.Name == "Connection")
.Select(p => p.GetValue(sender, null))
.SingleOrDefault();
var entityConn = (EntityConnection)conn;
var objStateManager = (ObjectStateManager)sender.GetType()
.GetProperty("ObjectStateManager", BindingFlags.Instance | BindingFlags.Public)
.GetValue(sender, null);
var workspace = entityConn.GetMetadataWorkspace();
var translatorT =
sender.GetType().Assembly.GetType("System.Data.Mapping.Update.Internal.UpdateTranslator");
var translator = Activator.CreateInstance(translatorT, BindingFlags.Instance |
BindingFlags.NonPublic, null, new object[] {objStateManager,workspace,
entityConn,entityConn.ConnectionTimeout }, CultureInfo.InvariantCulture);
var produceCommands = translator.GetType().GetMethod(
"ProduceCommands", BindingFlags.NonPublic | BindingFlags.Instance);
var commands = (IEnumerable<object>)produceCommands.Invoke(translator, null);
foreach (var cmd in commands)
{
var identifierValues = new Dictionary<int, object>();
var dcmd =
(DbCommand)cmd.GetType()
.GetMethod("CreateCommand", BindingFlags.Instance | BindingFlags.NonPublic)
.Invoke(cmd, new[] { translator, identifierValues });
foreach (DbParameter param in dcmd.Parameters)
{
var sqlParam = (SqlParameter)param;
commandText.AppendLine(String.Format("declare {0} {1} {2}",
sqlParam.ParameterName,
sqlParam.SqlDbType.ToString().ToLower(),
sqlParam.Size > 0 ? "(" + sqlParam.Size + ")" : ""));
commandText.AppendLine(String.Format("set {0} = '{1}'", sqlParam.ParameterName, sqlParam.SqlValue));
}
commandText.AppendLine();
commandText.AppendLine(dcmd.CommandText);
commandText.AppendLine("go");
commandText.AppendLine();
}
System.Diagnostics.Debug.Write(commandText.ToString());
}
短期間のロギングの場合、DbContextコンストラクターに配置します。
Database.Log = x => Debug.WriteLine(x);
SQLのログの追加/削除が非常に高速です。長期間使用する場合は、次のようにチェックでラップできます。
#IFDEF DEBUG // or something similar
インターセプターを使用してEF6を使用して生成された実際のSQL(後で再生する可能性がある)をキャプチャする場合は、次の操作を実行できます。
インターセプターの作成
public class InsertUpdateInterceptor : IDbCommandInterceptor
{
public virtual void NonQueryExecuting(
DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
logCommand(command);
}
public virtual void ReaderExecuting(
DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
// this will capture all SELECT queries if you care about them..
// however it also captures INSERT statements as well
logCommand(command);
}
public virtual void ScalarExecuting(
DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
logCommand(command);
}
private void logCommand(DbCommand dbCommand)
{
StringBuilder commandText = new StringBuilder();
commandText.AppendLine("-- New statement generated: " + System.DateTime.Now.ToString());
commandText.AppendLine();
// as the command has a bunch of parameters, we need to declare
// those parameters here so the SQL will execute properly
foreach (DbParameter param in dbCommand.Parameters)
{
var sqlParam = (SqlParameter)param;
commandText.AppendLine(String.Format("DECLARE {0} {1} {2}",
sqlParam.ParameterName,
sqlParam.SqlDbType.ToString().ToLower(),
getSqlDataTypeSize(sqlParam));
var escapedValue = sqlParam.SqlValue.replace("'", "''");
commandText.AppendLine(String.Format("SET {0} = '{1}'", sqlParam.ParameterName, escapedValue ));
commandText.AppendLine();
}
commandText.AppendLine(dbCommand.CommandText);
commandText.AppendLine("GO");
commandText.AppendLine();
commandText.AppendLine();
System.IO.File.AppendAllText("outputfile.sql", commandText.ToString());
}
private string getSqlDataTypeSize(SqlParameter param)
{
if (param.Size == 0)
{
return "";
}
if (param.Size == -1)
{
return "(MAX)";
}
return "(" + param.Size + ")";
}
// To implement the IDbCommandInterceptor interface you need to also implement these methods like so
public void NonQueryExecuted(
DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
}
public void ReaderExecuted(
DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
}
public void ScalarExecuted(
DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
}
}
また、インターセプターも登録する必要があります。 ASP.NETアプリケーション内でこれを実行している場合は、一度だけ実行してください。そうしないと、同じ要求を複数回インターセプトすることになります。
例DAO
public class MyDataDAO
{
private static bool isDbInterceptionInitialised = false;
public MyDataDAO()
{
if (!isDbInterceptionInitialised)
{
DbInterception.Add(new InsertUpdateInterceptor());
isDbInterceptionInitialised = true;
}
}
public void Insert(string dataToInsert)
{
using (myentities context = new myentities())
{
MyData myData = new MyData();
myData.data = dataToInsert;
// this will trigger the interceptor
context.SaveChanges();
}
}
}
これは同じことを行いますが、コンテキストを使用するたびに、出力ウィンドウにsqlクエリが書き込まれます。違いは、リリースではコンパイルされないことです。
public MyEntitities()
: base()
{
Database.Log = s => System.Diagnostics.Trace.WriteLine(s);
}
EF6用にトムリーガンのコードが更新されました。
public void objContext_SavingChanges(object sender, EventArgs e)
{
var commandText = new StringBuilder();
var conn = sender.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.Name == "Connection")
.Select(p => p.GetValue(sender, null))
.SingleOrDefault();
var entityConn = (EntityConnection)conn;
var objStateManager = (System.Data.Entity.Core.Objects.ObjectStateManager)sender.GetType()
.GetProperty("ObjectStateManager", BindingFlags.Instance | BindingFlags.Public)
.GetValue(sender, null);
var workspace = entityConn.GetMetadataWorkspace();
var translatorT =
sender.GetType().Assembly.GetType("System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator");
var entityAdapterT =
sender.GetType().Assembly.GetType("System.Data.Entity.Core.EntityClient.Internal.EntityAdapter");
var entityAdapter = Activator.CreateInstance(entityAdapterT, BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public, null, new object[] { sender }, System.Globalization.CultureInfo.InvariantCulture);
entityAdapterT.GetProperty("Connection").SetValue(entityAdapter, entityConn);
var translator = Activator.CreateInstance(translatorT, BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public, null, new object[] { entityAdapter }, System.Globalization.CultureInfo.InvariantCulture);
var produceCommands = translator.GetType().GetMethod(
"ProduceCommands", BindingFlags.NonPublic | BindingFlags.Instance);
var commands = (IEnumerable<object>)produceCommands.Invoke(translator, null);
foreach (var cmd in commands)
{
var identifierValues = new Dictionary<int, object>();
var dcmd =
(System.Data.Common.DbCommand)cmd.GetType()
.GetMethod("CreateCommand", BindingFlags.Instance | BindingFlags.NonPublic)
.Invoke(cmd, new[] { identifierValues });
foreach (System.Data.Common.DbParameter param in dcmd.Parameters)
{
var sqlParam = (SqlParameter)param;
commandText.AppendLine(String.Format("declare {0} {1} {2}",
sqlParam.ParameterName,
sqlParam.SqlDbType.ToString().ToLower(),
sqlParam.Size > 0 ? "(" + sqlParam.Size + ")" : ""));
commandText.AppendLine(String.Format("set {0} = '{1}'", sqlParam.ParameterName, sqlParam.SqlValue));
}
commandText.AppendLine();
commandText.AppendLine(dcmd.CommandText);
commandText.AppendLine("go");
commandText.AppendLine();
}
System.Diagnostics.Debug.Write(commandText.ToString());
}
これが役立つはずです、EFTracingProvider
SQL Server Profilerを使用して、接続先のデータベースサーバーに対して実行できます。