1. 程式人生 > >Entity Framework——記錄執行的命令信息

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

信息 實現 container times 上下文 static sed vid ise

有兩種方法可以記錄執行的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

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

轉載與引用請註明出處。

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

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