我怎样才能从我的程序中的DbContext.SaveChanges()生成SQL?

根据这个线程,我们可以通过EFlogging生成的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

http://code.msdn.microsoft.com/EFProviderWrappers

这是做同样的事情,但是每次你使用你的上下文时,它都会在输出窗口中写入sql查询。 不同之处在于它不能在发行版中编译。

 public MyEntitities() : base() { Database.Log = s => System.Diagnostics.Trace.WriteLine(s); } 

这个StackOverflow解释了跟踪和debugging之间的区别。