如何在我的程序中从DbContext.SaveChanges()记录生成的SQL?

Mas*_*oud 77 c# sql logging entity-framework dbcontext

根据这个帖子,我们可以记录生成的SQLvia EF,但是怎么样DbContext.SaveChanges()?没有任何额外的框架,有没有简单的方法来完成这项工作?

Lor*_*ler 124

在实体框架6.0中,Database类具有属性Action<string> Log.因此,设置日志记录非常简单:

context.Database.Log = Console.WriteLine;
Run Code Online (Sandbox Code Playgroud)

对于更高级的需求,您可以设置拦截器.有关实体框架wiki的更多信息

  • 或者把它放在DbContext ctor'Database.Log = s => Debug.WriteLine(s);' (67认同)
  • 检查了所有的答案,但没有看到; 我们如何在EF Core中执行此操作? (7认同)
  • Public Sub New()Database.Log = Sub(s)Debug.WriteLine(s)End Sub End Sub (2认同)
  • 对于 `Serilog` 用户来说,这同样简单,例如:`context.Database.Log = Log.Logger.Verbose;`(替换你的 `Serilog.ILogger` 实例)。 (2认同)

Tom*_*gan 15

请参阅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
Run Code Online (Sandbox Code Playgroud)

上下文的构造函数挂钩了SavingChanges事件(我只想对调试版本进行昂贵的反射):

public MyDbContext(): base("MyDbContext")
{
#if DEBUG
    ((IObjectContextAdapter)this).ObjectContext.SavingChanges += new EventHandler(objContext_SavingChanges);
#endif
}
Run Code Online (Sandbox Code Playgroud)

保存更改事件将生成的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());
    }
Run Code Online (Sandbox Code Playgroud)


TPA*_*OPA 10

对于短期日志记录,我只是放入DbContext构造函数:

Database.Log = x => Debug.WriteLine(x);
Run Code Online (Sandbox Code Playgroud)

添加/删除SQL的日志记录非常快.对于长期使用期限,可以用支票包裹

#IFDEF DEBUG // or something similar
Run Code Online (Sandbox Code Playgroud)


Roc*_*lan 8

如果要捕获使用拦截器使用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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

而且你还需要注册你的拦截器.如果您在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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

62445 次

最近记录:

6 年,5 月 前