EF6学习笔记三十一:性能优化(二)

要专业系统地学习EF推荐《你必须掌握的Entity Framework 6.x与Core 2.0》。这本书作者(汪鹏,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/

 本次学习get到的知识点有这些

减少首次与数据库交互的代码

避免数据库版本查询

单连接多请求

AsNoTracking禁用变更追踪

关闭数据库初始化

这个对性能造成的影响很小,关闭这个更多的是学术上的正确性,实际并不会有明显的性能提升

当我们打印数据库日志,会看到执行了这样一些查询

这个显然不是我们的自己的查询操作,但是它查询了这些是做什么的?我没有深究

如何关闭,两种方式,直接在上下文的构造函数添加设置或者写一个派生自DbConfiguration的类

public EFDbContext() : base("connstr")
{
      Database.SetInitializer<EFDbContext>(null);
}

数据库初始化策略来复习下

public class EFDbContext:DbContext
    {
        public EFDbContext()
        {
            //  如果数据库不存在就创建
            //Database.SetInitializer(new CreateDatabaseIfNotExists<EFDbContext>());

            //  总是创建数据库
            //Database.SetInitializer(new DropCreateDatabaseAlways<EFDbContext>());

            // 如果model变了就创建数据库
            //Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EFDbContext>());

            //禁用数据库初始化策略
            Database.SetInitializer<EFDbContext>(null);
        }
}

我每个都用了看了下,觉得没什么作用啊。

避免数据库版本查询

这个问题是这样的,查询时,因为没办法确定从DbConnection定向的SQL server版本,所以EF需要生成事件来确定目标SQL Server的版本。

这里就有问题了,没办法知道数据版本,这里的数据库指的MS SQL 还是所有EF支持的数据库?

如果单单指的是MS SQL,那么这都是微软一手搞的,怎么这个还要去查询,不应该是直接就知道吗?

利用SQLProfiler来追踪那条查询数据库版本的SQL语句看看

我们把这条SQL语句拿到数据库中执行看一下是什么

结果是4,但是不知道是什么意思。EngineEdition 确实是“引擎版本”的意思

查询版本难道不是select @@version?

我数据库安装的是2017,数据库版本是14,数据库管理工具是17.x

这个和my sql很像了,现在都是分开安装了,my sql 没有提供管理工具,可以使用比如navicat这种工具

那么就卡看看你serverproperty这个方法到底是什么意思

serverproperty()方法查询的是数据库实例的属性信息,关于更多的属性可以去很多人的博客里去看看:https://www.cnblogs.com/onesmail/p/4533714.html

这里具体看下EngineEdition属性

我安装的确实是Express专业版的,现在才知道他查询出来的是这个东西

select serverproperty('productversion')

上面这个属性才是我说的“版本”

那么怎样让EF不再查询SQL版本?如果我们明确知道数据版本,可以完全返回一个硬编码值,从而消除查询数据库版本的事件

来看看作者交给我们的写法

创建一个继承自IManifestTokenResolver的类,使用ResolverManifestToken方法,返回我们已经的SQL Server版本。这个类需要在EF中进行注册

这个ManifestToken是不是很熟,前面我们弄到Edmx文件,它里面就有,不过我不知道他们是不是相等的。

edmx是一个配置文件,那么现在通过代码来是不是和在edmx文件中配置是一样的呀?

public class ManifestTokenResolver : IManifestTokenResolver
    {
        private readonly IManifestTokenResolver _defaultResolver = new DefaultManifestTokenResolver();
        public string ResolveManifestToken(DbConnection connection)
        {
            // 进入了三次
            if (connection is SqlConnection sqlConn)
            {
                return "2008";
            }
            else
            {
                string version = _defaultResolver.ResolveManifestToken(connection);  //  2012
                return version;
            }
        }
    }
public class EFConfiguration:DbConfiguration
    {
        public EFConfiguration()
        {
            SetManifestTokenResolver(new ManifestTokenResolver());
        }
    }

这里就有问题了,为什是2012?然后我return “2008”,也没有出现任何问题。

这里真的不解,但是,确实EF没有再查询数据库版本了。

我们来生成edmx文件,看一下里面的配置

这是我们通过代码设置的“2008”,但是还是弄不懂为什么default是“2012”

单连接多请求

关于这一点我完全没有认识,作者是这样说的:EF实体框架支持多个结果集,允许通过单个连接对SQL Server进行多个请求,从而减少往返次数。如果应用程序服务器和数据库之间存在高延迟,这一点尤其有用。

我开始是这样理解的,因为我打印数据库日志看到,每次一查询 都会开启关闭一次数据连接。那么单连接多请求是不是当我从数据库查询出两个实体他只会开启关闭一次connection呢?

于是按照作者提供的配置

 但是不是,应该是我理解错了。

这个应该是说的是连接复用:https://www.cnblogs.com/icebutterfly/p/9254521.html

意思是这样,当用户a提交一个请求过来,用户B也提交一个请求,那么如果支持MARS的话,可以连接复用,他们之间共用一个数据库连接。

然后我也跟着博客里面说的去弄,发现还是开启了多个连接。难道这个东西真的如博主所说,目前只适用于Sql Server 2005?连接复用的问题我也就止步在这了。

AsNoTracking

 这个在前面作者也说到了。EF操作到的实体需要被EF进行追踪,比如查询出的实体状态为Unchanged、修改后实体状态为Modified

当我们提供一个查询的接口,比如只是查询学生信息

那么我们可以这样写

var stu1 = ctx.Students.AsNoTracking().ToList();

因为这些实体只是查询出来,不会再有其他的操作,那么我们取消实体的追踪,性能会好一些。

但是如果你要修改,那就要重新让EF追踪了。这个还是比较简单的。这个方法还是比较不错的,问题他只作用在要操作的实体。

我们前面了解到通过一个开关可以关闭变更追踪:Configuration.AutoDetectChangesEnabled = false;

但是这个一设置在整个上下文中就生效了,如果不是对它特别了解,随便使用可能造成意想不到的问题。 

原文地址:https://www.cnblogs.com/jinshan-go/p/10421080.html