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。

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

转载与引用请注明出处。

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

原文地址:https://www.cnblogs.com/hdwgxz/p/8487945.html

时间: 2024-11-05 11:34:05

Entity Framework——记录执行的命令信息的相关文章

[翻译] - &lt;Entity Framework&gt; - 直接执行数据库命令

原文:[翻译] - <Entity Framework> - 直接执行数据库命令 纯属学习上的记录, 非专业翻译, 如有错误欢迎指正! 原文地址: http://msdn.microsoft.com/en-us/library/gg715124(v=vs.103) 使用 EF 4.1 或者更新版本, 你可以直接执行任何数据库命令. 在本节介绍的方法允许你对数据库执行原生的 SQL 命令. 通过 SQL 查询语句获取实体对象集 DbSet 类中的 SqlQuery 方法允许你执行一个返回实体对象

关于MySql entity framework 6 执行like查询问题解决方案

本人不善于言辞,直接开门见山 环境:EF6.0.0.0+MySQL Server5.6+MySqlConnector6.9.5.0 问题点如下: 1 var username = "admin"; 2 var lst = userService.GetQuery().Where(p => p.UserName.Contains(username)); 3 foreach (var user in lst) 4 { 5 Console.WriteLine(user.Id); 6 7

MVC5 Entity Framework学习之弹性连接和命令拦截

到目前为止,应用程序一直在本地IIS Express上运行.如果你想让别人通过互联网访问你的应用程序,你必须将它部署到WEB服务器同时将数据库部署到数据库服务器 本篇文章中将教你如何使用在将你的应用程序部署到云环境时的Entity Framework 6的非常有价值的两个特性:弹性连接(瞬时错误的自动重试)和命令拦截(捕获所有发送到数据库的SQL查询语句并记录至日志中). 1.启用弹性连接 当你将应用程序部署到Windows Azure时,相应的数据库部也应被部署到Windows Azure S

Entity Framework 6.1-Code First【转】

Entity Framework 6.1-Code First 分类: Entity Framework 2014-04-21 14:56 2034人阅读 评论(0) 收藏 举报 entityentity framework框架 Code First-代码优先,先创建好领域模型.新建MyDbContext继承DbContext.根据代码自动生成数据库 Code First优点 1.可以自由的创建领域模型,基本不受EF框架的限制.自由的命名.程序员只需要关心对象间的关系.基本做到了与数据库的完全分

MVC5 Entity Framework学习之处理并发

之前你已经学习了如何更新数据,那么在本节你将学习如何在当多个用户在同一时间更新同一实体时处理冲突. 修改与Department实体相关的那些页面以便它们能够i处理并发错误.下面的截图是Index 和Delete页面,以及当出现并发冲突时的错误消息. 并发冲突 当一个用户对实体的数据进行编辑,然后另一个用户在前一个用户将更改写入到数据库之前更新同一实体的数据时将发生并发冲突.如果你没有启用冲突检测,那么最后一次对数据库的更新将会覆盖其他用户对数据库所做的更改.在大部分应用程序中,这种风险是可以接受

MVC5 Entity Framework学习之异步和存储过程

在之前的文章中,你已经学习了如何使用同步编程模型来读取和更新数据,在本节中你将学习如何实现异步编程模型.异步可以使应用程序执行更有效率,因为它可以更有效的使用服务器资源. 同样在本节中你还将学习如何针对实体的insert, update, 和delete操作使用存储过程. 最后将应用程序部署到 Windows Azure. 下面是完成后的页面 为什么要使用异步代码 一个web服务器的可用线程是有限的,在高负载情况下,所有的可用线程可能都在被使用.当出现这种情况时,服务器将无法处理新的请求,直到有

Entity Framework应用:Code First模式数据迁移的基本用法

使用Entity Framework的Code First模式在进行数据迁移的时候会遇到一些问题,熟记一些常用的命令很重要,下面整理出了数据迁移时常用的一些命令. 一.模型设计 EF默认使用id字段作为主键,如果没有,则需要指定主键. 二.数据迁移基本命令和常用参数 1.安装Entity Framework a.使用命令安装:visual studio工具栏->工具->NuGet 程序包管理器->程序包管理器控制台 输入命令:Install-Package EntityFramework

Entity Framework 6.X实现记录执行的SQL功能

Entity Framework在使用时,很多时间操纵的是Model,并没有写sql语句,有时候为了调试或优化等,又需要追踪Entity framework自动生成的sql(最好还能记录起来,方便出错时排查) 方式一: 通过System.Data.Entity.DataBase.Log属性指定一个无返回值的委托,来实现记录日志的功能 public partial class EFContext<T> : DbContext where T : class { public EFContext(

《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-9  关联实体过滤和排序 问题 你有一实体的实例,你想加载应用了过滤和排序的相关实体. 解决方案 假设你有如图5-24所示的概念模型 图5-24 一个酒店预定系统的模型 假设我们有一个酒店(Hotel)实体,使用代码清单5-22,获取酒店的商务套房(executive suite),查看是否被预定,并按房价排序. 代码清单5-22.通过方法Entry()和Query()显式加载实体集合,