我怎样才能从我的程序中的DbContext.SaveChanges()生成SQL?
根据这个线程,我们可以通过EF
logging生成的SQL
,但是DbContext.SaveChanges()
呢? 有没有简单的方法来做这个工作,没有任何额外的框架?
在entity framework6.0中,Database类有一个属性Action<string> Log
。 所以设置日志logging就像:
context.Database.Log = Console.WriteLine;
对于更高级的需求,你可以设置一个拦截器 。 有关entity frameworkwiki的更多信息
见http://www.codeproject.com/Articles/499902/Profiling-Entity-Framework-5-in-code 。 我使用Code First,POCO DbContext,Entity Framework 5在一个asp.net mvc应用程序中实现了Cook先生的想法。
应用程序的上下文类来自DbContext:
public class MyDbContext : DbContext
上下文的构造函数挂钩了SavingChanges事件(我只想为debugging版本做昂贵的reflection):
public MyDbContext(): base("MyDbContext") { #if DEBUG ((IObjectContextAdapter)this).ObjectContext.SavingChanges += new EventHandler(objContext_SavingChanges); #endif }
保存更改事件将生成的sql写入输出窗口。 我从库克先生复制的代码将DbParameter转换为一个SqlParamter,我保留原来的状态,因为我打了一个Sql Server,但是我假设如果您打击其他types的数据库,转换将失败。
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()); }
如果要捕获使用拦截器使用EF6生成的实际SQL(可能稍后回放),则可以执行以下操作。
创build你的拦截器
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(); } } }
对于短期logging,我只是把它放在DbContext构造函数中:
Database.Log = x => Debug.WriteLine(x);
很快就可以添加/删除SQL的日志logging。 长期使用期限,可以用支票包裹
#IFDEF DEBUG // or something similar
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 Profiler并针对要连接的数据库服务器运行它。
这应该有助于EFTracingProvider
这是做同样的事情,但是每次你使用你的上下文时,它都会在输出窗口中写入sql查询。 不同之处在于它不能在发行版中编译。
public MyEntitities() : base() { Database.Log = s => System.Diagnostics.Trace.WriteLine(s); }
这个StackOverflow解释了跟踪和debugging之间的区别。