내 프로그램의 DbContext.SaveChanges ()에서 생성 된 SQL을 어떻게 기록 할 수 있습니까?
이 질문에 이미 답변이 있습니다.
에 따르면 이 스레드, 우리는 생성 로그인 할 수 있습니다 SQL
통해 EF
,하지만 무엇에 대해 DbContext.SaveChanges()
? 추가 프레임 워크없이이 작업을 쉽게 수행 할 수있는 방법이 있습니까?
엔티티 프레임 워크 6.0에서 Database 클래스에는 속성이 Action<string> Log
있습니다. 따라서 로깅 설정은 다음과 같이 쉽습니다.
context.Database.Log = Console.WriteLine;
고급 요구 사항을 위해 인터셉터를 설정할 수 있습니다 .
http://www.codeproject.com/Articles/499902/Profiling-Entity-Framework-5-in-code를 참조 하십시오 . 저는 Code First, POCO DbContext, Entity Framework 5를 사용하여 asp.net mvc 응용 프로그램에서 Mr. Cook의 아이디어를 구현했습니다.
애플리케이션의 컨텍스트 클래스는 DbContext에서 파생됩니다.
public class MyDbContext : DbContext
컨텍스트의 생성자는 SavingChanges 이벤트를 연결합니다 (디버그 빌드에 대해 값 비싼 리플렉션 만 수행하고 싶습니다).
public MyDbContext(): base("MyDbContext")
{
#if DEBUG
((IObjectContextAdapter)this).ObjectContext.SavingChanges += new EventHandler(objContext_SavingChanges);
#endif
}
변경 사항 저장 이벤트는 생성 된 SQL을 출력 창에 씁니다. Mr. Cook에서 복사 한 코드는 DbParameter를 SqlParamter로 변환합니다. Sql 서버를 사용하고 있기 때문에 그대로 둡니다. 그러나 다른 종류의 데이터베이스를 사용하면 변환이 실패 할 것이라고 가정합니다.
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);
}
이 StackOverflow는 추적과 디버그의 차이점을 설명합니다.
Tom Regan의 코드가 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());
}
SQL Server 프로파일 러를 사용하고 연결중인 데이터베이스 서버에 대해 실행할 수 있습니다.
This should help, the EFTracingProvider
'Development Tip' 카테고리의 다른 글
MVVM의 기본 개념-ViewModel은 무엇을해야합니까? (0) | 2020.10.18 |
---|---|
사전에 튜플 목록 (0) | 2020.10.18 |
일반 배열에서 동일한 키와 값을 사용하여 assoc 배열 만들기 (0) | 2020.10.18 |
문자열에 문자 만 포함되어 있는지 확인 (0) | 2020.10.17 |
jarsigner : jar에 서명 할 수 없음 : java.util.zip.ZipException : 잘못된 항목 압축 크기 (예상 값 463이지만 465 바이트 있음) (0) | 2020.10.17 |