Entity Framework——記錄執行的命令信息


有兩種方法可以記錄執行的SQl語句:

  • 使用DbContext.Database.Log屬性
  • 實現IDbCommandInterceptor接口

使用DbContext.Database.Log屬性

下面截圖顯示了Database屬性和Log屬性,可以看出這個屬性是一個委托,類型為Action<string>

Log屬性的解釋為:

Set this property to log the SQL generated by the System.Data.Entity.DbContext to the given delegate. For example, to log to the console, set this property to System.Console.Write(System.String).

使用方法:

1)在自定義上下文中獲得執行的SQL相關信息,即在自定上下文的構造函數中使用Database.Log

 

    /// <summary>
    /// 自定義上下文
    /// </summary>
    [DbConfigurationType(typeof(MySqlEFConfiguration))]
    public class CustomDbContext : DbContext
    {
        public CustomDbContext()
            : base("name=Master")
        {
            
            //this.Configuration.LazyLoadingEnabled = false;
            //new DropCreateDatabaseIfModelChanges<CustomDbContext>()
            //new DropCreateDatabaseAlways<CustomDbContext>()
            Database.SetInitializer<CustomDbContext>(null);
            this.Database.Log = Log;
        }

        public DbSet<User> Users { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            EntityConfiguration.Set(modelBuilder);
        }

        private void Log(string cmd)
        {
            //或輸出到控制台
            //Console.Write(cmd);

            //或輸出到文件
            //using (StreamWriter sw = new StreamWriter(@"E:\EFCmdLogger.txt"))
            //{
            //    sw.WriteLine(cmd);
            //}

            //或輸出到調試信息窗口
            Debug.WriteLine(cmd);
        }
     }

 

執行結果如下截圖

 

2)在具體的方法中使用

 

    public class EFOPerations
    {

        public static void ReadUser()
        {
            Stopwatch stw = new Stopwatch();
            stw.Start();
            using (CustomDbContext db = new CustomDbContext())
            {
                db.Database.Log = Console.WriteLine;
                User user = db.Users.Find(1);
                var userDTO = new { Account = user.Account };
            }
            stw.Stop();
            var time = stw.ElapsedMilliseconds;
        }
    }

 

注意

db.Database.Log = Console.WriteLine;這條語句的位置;如果將其放到查詢語句,即User user = db.Users.Find(1);之后則無法輸出信息!

 

還可以改變日志的格式:

創建繼承自DatabaseLogFormatter的類,實現新的格式化器,然后使用

System.Data.Entity.DbConfiguration.SetDatabaseLogFormatter(System.Func<System.Data.Entity.DbContext,System.Action<System.String>,System.Data.Entity.Infrastructure.Interception.DatabaseLogFormatter>)

DatabaseLogFormatter的三個方法

LogCommand:在SQL 語句或存儲過程執行前記錄它。

LogParameter:記錄參數,默認被LogCommand調用(未能驗證這一點)

LogResult:記錄SQL 語句或存儲過程執行后的一些相關信息

這三個方法包含的參數為:

DbCommand commandSQL 語句或存儲過程相關的信息。

DbCommandInterceptionContext<TResult> interceptionContext:執行結果相關的信息。

DbParameter parameterSystem.Data.Common.DbCommand 的參數

重寫LogCommandLogResult都可以改變SQL 語句或存儲過程相關信息格式,但是注意這兩個方法interceptionContext參數的值可能會不一樣。

 

繼承DatabaseLogFormatter,實現格式化器

 

public class CustomDatabaseLogFormatter : DatabaseLogFormatter
    {
        public CustomDatabaseLogFormatter(DbContext context, Action<string> writeAction)
            : base(context, writeAction)
        {
        }
        public override void LogCommand<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
        {

        }

        public override void LogResult<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < command.Parameters.Count; i++)
            {
                sb.AppendLine(string.Format("參數名稱:{0},值:{1}", command.Parameters[0].ParameterName, command.Parameters[0].Value));
            }
            Write(command.CommandText + Environment.NewLine
                + command.CommandTimeout + Environment.NewLine
                + command.CommandType + Environment.NewLine
                + Environment.NewLine
                + sb.ToString());
        }
}

設置新的格式化器

public class CustomDbConfiguration : MySqlEFConfiguration
    {
        public CustomDbConfiguration():base()
        {
            //this.AddInterceptor(new CommandInterceptor(new Logger()));
            SetDatabaseLogFormatter((context, writeAction) => new CustomDatabaseLogFormatter(context, writeAction));
        }
}

使用自定義CustomDbConfiguration

[DbConfigurationType(typeof(CustomDbConfiguration))]
    public class CustomDbContext : DbContext
    {
        public CustomDbContext()
            : base("name=Master")
        {
            
            //this.Configuration.LazyLoadingEnabled = false;
            //new DropCreateDatabaseIfModelChanges<CustomDbContext>()
            //new DropCreateDatabaseAlways<CustomDbContext>()
            Database.SetInitializer<CustomDbContext>(null);
            this.Database.Log = Log;
        }

        ......

}    

 

實現IDbCommandInterceptor接口

實現IDbCommandInterceptor,同時為了靈活的記錄執行信息,定義了日志接口

 

public class CommandInterceptor : IDbCommandInterceptor
    {
        private ICommandLogger logger;
        public CommandInterceptor(ICommandLogger logger)
        {
            this.logger = logger;
        }
        public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            this.logger.Log<int>(command, interceptionContext);
        }

        public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            this.logger.Log<int>(command, interceptionContext);
        }

        public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
        {
            this.logger.Log<DbDataReader>(command, interceptionContext);
        }

        public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
        {
            this.logger.Log<DbDataReader>(command, interceptionContext);
        }

        public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            this.logger.Log<object>(command, interceptionContext);
        }

        public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            this.logger.Log<object>(command, interceptionContext);
        }
    }

    public interface ICommandLogger
    {
        void Log<T>(DbCommand command, DbCommandInterceptionContext<T> interceptionContext);
}

public class Logger : ICommandLogger
    {
        public void Log<T>(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<T> interceptionContext)
        {
            StringBuilder sb = new StringBuilder();
            for(int i =0;i<command.Parameters.Count;i++)
            {
                sb.AppendLine(string.Format("參數名稱:{0},值:{1}", command.Parameters[0].ParameterName, command.Parameters[0].Value));
            }
            
            Debug.WriteLine(command.CommandText+Environment.NewLine
                + command.CommandTimeout + Environment.NewLine
                + command.CommandType + Environment.NewLine
                + Environment.NewLine
                + sb.ToString());
        }
    }

 

 

如何使用這兩個類呢?

1使用配置文件

<entityFramework>
    <interceptors>
      <interceptor type="ConsoleApp_EntityFramework.Interceptor.CommandInterceptor, ConsoleApp_EntityFramework.Interceptor">
      </interceptor>
    </interceptors>
 </entityFramework>

但是采用這種方式要對上面的CommandInterceptor 進行改造。

 

public class CommandInterceptor : IDbCommandInterceptor
    {
        private ICommandLogger logger;
        public CommandInterceptor()
        {
            this.logger = new Logger();
        }

    ......
}

 

但是如果EF操作的是Mysql那么這種方法不行,拋出異常:無法識別的元素“interceptors”

2編碼方式

只有上面兩個類還不夠,還要定義創建一個繼承自DbConfiguration的配置類

public class CustomDbConfiguration : DbConfiguration
    {
        public CustomDbConfiguration():base()
        {
            this.AddInterceptor(new CommandInterceptor(new Logger()));
        }
}

在自定義數據庫上下文上使用此特性

 

    /// <summary>
    /// 自定義上下文
    /// </summary>
    [DbConfigurationType(typeof(CustomDbConfiguration))]
    public class CustomDbContext : DbContext
    {
        ......
    }

 

一切准備好后運行程序,卻拋出異常:

The ADO.NET provider with invariant name 'MySql.Data.MySqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.

似乎是MySql.Data.MySqlClient的問題,其實不是!

如果是SQL Server則沒問題,但這里EF框架操作的是MySql,要是使用MySql.Data.Entity.MySqlEFConfiguration這個類,而不是System.Data.Entity.DbConfiguration,所以CustomDbConfiguration應該派生自MySql.Data.Entity.MySqlEFConfiguration

    public class CustomDbConfiguration : MySqlEFConfiguration
    {
        public CustomDbConfiguration():base()
        {
            this.AddInterceptor(new CommandInterceptor(new Logger()));
        }
        .....
    }

這樣修改后,運行程序得到下面的結果:

可以看到日志打印了兩次,這是因為ReaderExecutingReaderExecuted各調用了一次,執行的順序是先ReaderExecuting然后ReaderExecuted

 

-----------------------------------------------------------------------------------------

轉載與引用請注明出處。

時間倉促,水平有限,如有不當之處,歡迎指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM