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 command:SQL 语句或存储过程相关的信息。

DbCommandInterceptionContext<TResult> interceptionContext:执行结果相关的信息。

DbParameter parameter:System.Data.Common.DbCommand 的参数。

重写LogCommand或LogResult都可以改变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()));
        }
        .....
    }

这样修改后,运行程序得到下面的结果:

可以看到日志打印了两次,这是因为ReaderExecuting和ReaderExecuted各调用了一次,执行的顺序是先ReaderExecuting然后ReaderExecuted。

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

转载与引用请注明出处。

时间仓促,水平有限,如有不当之处,欢迎指正。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IT笔记

SpringBoot开发案例之JdbcTemplate批量操作

前言 在我们做后端服务Dao层开发,特别是大数据批量插入的时候,这时候普通的ORM框架(Mybatis、hibernate、JPA)就无法满足程序对性能的要求了...

5159
来自专栏Linyb极客之路

spring多数据源实现

由该代码片段我们可以很直观的发现,这个方法的作用就是用来实现查找目标数据源,通过代码我们可以查找数据源是根据determineCurrentLookupKey(...

3804
来自专栏闻道于事

在静态方法中应用spring注入的类

最近在一次项目的重构中,原项目需要在静态方法中调用service,现在需要更换框架,service需要自动注入,无法再静态方法中调用

963
来自专栏Java技术栈

spring bean初始化及销毁你必须要掌握的回调方法。

spring bean在初始化和销毁的时候我们可以触发一些自定义的回调操作。 初始化的时候实现的方法 1、通过java提供的@PostConstruct注解; ...

31912
来自专栏黑泽君的专栏

day33_Spring学习笔记_01

4 + 1:4个核心jar包(beans、core、context、expression)+ 1个依赖jar包(com.springsource.org.apa...

641
来自专栏史上最简单的Spring Cloud教程

Spring详解篇之 AOP面向切面编程

一、概述 Aop(aspect oriented programming面向切面编程),是spring框架的另一个特征。AOP包括切面、连接点、通知(advic...

2047
来自专栏QQ音乐技术团队的专栏

一种避免 iOS 内存碎片的方法

CFStringCreateWithBytes 方法分配的字符串是堆空间,如果数据过长,则很容易产生内存碎片。解决这个问题有两种思路:一是在栈空间分配内存,二是...

7960
来自专栏程序员的SOD蜜

PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug

最近将公司的项目从SqlServer移植到PostgreSQL数据库上来,在调用数据库的存储过程(自定义函数)的时候,发现一个奇怪的问题,老是报函数无法找到。 ...

3027
来自专栏IT 指南者专栏

Spring框架系列之AOP思想

微信公众号:compassblog 欢迎关注、转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1、AOP概述 (1)、什么是 AOP AOP 为 As...

3687
来自专栏大内老A

.NET的资源并不限于.resx文件,你可以采用任意存储形式[下篇]

在《上篇》中我们谈到ResourceManager在默认的情况下只能提供对内嵌于程序集的.resources资源文件的存取。为了实现对独立二进制.resourc...

1996

扫码关注云+社区

领取腾讯云代金券