EntityFramework之监听者判断SQL性能指标

前言

当我们利用EF这个ORM框架时,我们可能会利用LINQ或者原生的SQL语句来进行数据操作,此时我们无法确定我们的代码是否会给数据库带来一定的负载,当给数据库带来一定的压力时,由于项目中对数据进行相应的操作实在是太多,我们无法准确的去进行定位,又或者我们不是专业的DBA,无法准确的去分析SQL性能的优劣,此时该怎么办呢?我们完全不需要DBA,我们可以通过相应的操作来判断一段SQL代码的好坏,这就是我们本节需要讲的内容,利用EF中监听者来判断SQL性能,在之前系列中也有提到,可以参考之前系列。我们进入主题。

DbCommandInterceptor

不用讲从字面意思我们就能立马明白大概是【监听命令】,我们看下该类,如下:

我们无需多加细看,看这几个虚方法我们马上就能明白和我们之前猜测的一致,就是进行数据库操作的SQL命令。在这个类中有一个重要的类那就是 DbCommandInterceptionContext ,我们姑且叫做监听SQL命令的上下文吧,我们再看这个类中包含什么。如下:

在这个类中有一个重要的属性 UserState ,哦,意思是用户状态,根据摘要信息得知,我们可以设置我们进行操作的相关信息,同时还是个object类型,看来是利于对象之间的转换而给。

接下来进入主题,我们如何去判断SQL性能指标呢?答案:我们检索出执行SQL时以及执行SQL完成后的消耗时间即可。

SQL性能判断指标

那么问题来了,我们该如何正确这个时间呢?此问题又可以细分为两个步骤。

(1)如何知道SQL命令是正在执行时以及执行完成呢?

(2)知道了之后我们又如何设置以及获取时间呢?

我们一一来划分,首先我建立一个类 SQLProfiler ,而此类肯定是继承于 DbCommandInterceptor ,所以代码就变成了这样。

    public class SQLProfiler : DbCommandInterceptor
    {
        public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            Executing(interceptionContext);
            base.ReaderExecuting(command, interceptionContext);
        }

        public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            Executed(command, interceptionContext);
            base.ReaderExecuted(command, interceptionContext);
        }

        public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            Executing(interceptionContext);
            base.NonQueryExecuting(command, interceptionContext);
        }

        public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            Executed(command, interceptionContext);
            base.NonQueryExecuted(command, interceptionContext);
        }

        public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            Executing(interceptionContext);
            base.ScalarExecuting(command, interceptionContext);
        }

        public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            Executed(command, interceptionContext);
            base.ScalarExecuted(command, interceptionContext);
        }
}

貌似发现了什么,好像方法参数都是什么DbCommand和DbCommandInterCeptionContext,既然这样,我们依样画葫芦诺,为了从执行SQL命令开始,我们此时从这里开始计时,将计时对象给UserState即可,于是就有了下面的代码。

        private void Executing<T>(DbCommandInterceptionContext<T> interceptionContext)
        {
            var timer = new Stopwatch();
            interceptionContext.UserState = timer;
            timer.Start();
        }

此时应该就明朗了,我们在执行完成后来获取该计时对象并利用是否出现异常和我们指定设置的时间来判断其最终所花费的时间。执行完成后代码如下:

        private void Executed<T>(DbCommand command, DbCommandInterceptionContext<T> interceptionContext)
        {
            var timer = (Stopwatch)interceptionContext.UserState;
            timer.Stop();

            if (interceptionContext.Exception != null)
            {
                File.AppendAllLines(
                    _logFile,
                    new string[]
                {
                "错误SQL语句",
                interceptionContext.Exception.Message,
                command.CommandText,
                Environment.StackTrace,
                string.Empty,
                string.Empty,
                });
            }
            else if (timer.ElapsedMilliseconds >= _executionTime)
            {
                File.AppendAllLines(
                    _logFile,
                    new string[]
                {
                string.Format("耗时SQL语句({0}ms)",timer.ElapsedMilliseconds),
                command.CommandText,
                Environment.StackTrace,
                string.Empty,
                string.Empty,
                });
            }
        }

上述 _executionTime 是我们在此类构造函数中所设置的时间,构造函数如下:

        private readonly string _logFile;
        private readonly int _executionTime;

        public SQLProfiler(string logFile, int executionTime)
        {
            _logFile = logFile;
            _executionTime = executionTime;
        }

而logFile则是我么所要输出的日志。此时别忘记最重要的一件事,那就是DbConfiguration配置类中进行注册(或者在配置文件中进行注册)。如下:

    public class MyDbConfiguration : DbConfiguration
    {
        public MyDbConfiguration()
        {
            this.AddInterceptor(new SQLProfiler(@"D:\log.txt", 1));
        }
    }

接下来我们来进行检验结果。

检验成果

           using (var ctx = new EntityDbContext())
            {
                SqlParameter[] parameter = { };
                ctx.Database.SqlQuery<Student>("select * from a", parameter).ToList();
                Console.ReadKey();
            }

上述表a实际上是不存在的,我们就是要看看是否能检测到该异常并获取其时间。来,瞧一瞧。

【效果一】

【效果二】

结语

当然,如上述当执行查询时此表不存在肯定是会报错,但是我们的侧重点不在此,这里验证了此表不存在,同时也验证了SQL在执行时和执行完之间的耗时,同时我们可以通过构造函数手动设置当耗时大于我们预期的多少才算需要改善SQL。

 

时间: 2024-09-21 15:39:03

EntityFramework之监听者判断SQL性能指标的相关文章

我要用判断sql查到的时间距离现在是否3天后。

问题描述 我要用判断sql查到的时间距离现在是否3天后. String queryString = "SELECT c.couponname,c.couponfee/100,c.pic1_path,date_format(c.endtime,'%Y-%c-%d') as endTime FROM couponpublish c,couponpool s WHERE s.pubid=c.pubid AND s.owner='"+userId+"' "; String

请教https://username:password@hostname.com这种URL形式会导致监听者捕获账户信息么?

问题描述 这样的URL实际在访问过程中是否会把用户名和密码直接明文传输给服务器呢?监听者是否有可能捕获到密码? 解决方案 解决方案二:你好,ftp协议是不对数据包加密的,账号密码都是显式的,本地测试可抓取账号密码.测试软件:filezillaserverFTP服务器:192.168.66.41FTP客户端:192.168.66.70账号密码:test/test账号/密码抓包工具:wireshark解决方案三:不会.https的本质是HTTP+SSL,URL仅出现在HTTP协议的请求行,也就是说它

我想问下怎么判断sql数据库中的内容为空集,而不是NULL啊

问题描述 我想问下怎么判断sql数据库中的内容为空集,而不是NULL啊 如图,这两个字段一个为NULL,一个什么也没有但不是NULL,怎么判断什么都没有的情况啊!!! 我知道这样判断NULL _variant_t str = m_pRecordset->GetCollect("MyOrder"); if (str.vt == VT_NULL) 求教 解决方案 什么没有的时候是空字符串.你可以判断bstr类型,然后比较字符串是否为""空字符串. 解决方案二: s

ios-kvo监听者模式,它需要用到哪几种设计模式?

问题描述 kvo监听者模式,它需要用到哪几种设计模式? 面试的时候面试官问的....................... 解决方案 observer模式 名字中就提到了 解决方案二: 你查到了吗,我也想知道有几种设计模式 解决方案三: 观察者模式.没有其他模式了.

如何判断sql字段中存在中文字符?

如何判断sql字段中存在中文字符?  代码如下 复制代码 declare @t table([Name] nvarchar(10)) insert @t select '好的啊!' insert @t select '12345好的' insert @t select '123' insert @t select 'sdff' select * from @t where PATINDEX('%[吖-座]%',[name])>0 /* Name        ----------  好的啊! 1

监听者-观察者模式

     在C++模式设计中,常用的一种模式设计方法就是监听者-观察者模式.每个监听者对象都把自己理解的事件注册到一个中心事件处理注册库,接收到消息后,中心事件处理注册库会把该消息分发到每个监听者对象.     下面是来自于c++ profressional中的代码: 首先是监听者类: Listener.h 和Listener.cpp,这是一个监听者基类,该类中定义了消息处理的纯虚函数,代码如下: #pragma once class Listener{public: Listener(void

Android中监听判断网络连接状态的方法_Android

在无网或网速差的状态下,没必要去连接服务器. 你可以使用 ConnectivityManager 来判断是否连到网络,以及网络类型. 判断是否有网络连接 下面的代码用ConnectivityManager查询是活动网络连接判断是否有Internet连接. 复制代码 代码如下: ConnectivityManager cm =         (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);  

在ASP中判断SQL语句是否执行成功

利用err对象: sql="insert into table(f1,f2) values('v1','v2')" conn.execute sql if err.number<>0 then response.write "出错了:"& err.description err.clear else response.write "OK" end if   

C# 怎么判断Sql Server2008可以正常链接?

问题描述 我的程序是需要链接SqlServer2008且无人值守的,C#.NET里面怎么才能做到启动时检测是否能正常链接SqlServer2008的指定数据库,如果连接失败就等一秒后再连,超过5次就报错启动失败?或者不报启动失败直接无限重连. 解决方案 解决方案二:好像没太直接的办法,就trycatch吧解决方案三:在链接字符串里面设置Timeout(这点很重要,不然默认就是链接30秒才返回无法连接),然后你获取连接字符串的地方进行封装,可以自己写代码设置多少次,也可以用微软企业库的Transi