程序包android.support.annotation不存在_efcore技巧贴也许有你不知道的使用技巧

前言

.net 环境近些年也算是稳步发展。在开发的过程中,与数据库打交道是必不可少的。早期的开发者都是DbHelper一撸到底,到现在的各种各样的ORM框架大行其道。孰优孰劣谁也说不清楚,文无第一武无第二说的就是这个理。没有什么最好的,只有最适合你的。

本人也是从DbHelper开始,期间用过SugarSql,再到EFCODE。本着学习分享的初衷分享本人工作中总结的一些小技巧,希望能帮助更多开发者,期望能达到共同进步。文中若有错误地方,欢迎大家不吝赐教。

1. DbContext配置

在asp.net中,通常情况下,通过在Startup类的ConfigureServices方法中,将ef服务注入。

示例代码如下:

services.AddDbContext<DemoDbContext>(opt=>opt.UseMySql("server=.;Database=demo;Uid=root;Pwd=123;Port=3306;"));

以上代码表示使用MySql数据库。如果使用SqlServer数据库,可以把UseMySql改为UseSqlServer,其他数据库的使用方式也是通过调用不同的方法进行选择。但需要安装对应的扩展方法的程序包,如 Microsoft.EntityFrameworkCore.SqlServer 或 Microsoft.EntityFrameworkCore.Sqlite。

另外,UseMySql方法还包含了一个可空的Action 类型的参数,可以通过此参数进行一些个性化的配置,比如配置重试机制。如下所示:

services.AddDbContext<DemoDbContext>(opt => opt.UseMySql("server=.;Database=demo;Uid=root;Pwd=123456;Port=3306;",

provideropt => provideropt.EnableRetryOnFailure(3,TimeSpan.FromSeconds(10),new List(){0} )));

这个重试机制在某些场景下还是比较有用的。比如,由于网络波动或访问量导致的一瞬间的连接超时。如果不设置重试机制,则会直接触发异常,设置了超时后,则会根据设置的时间间隔以及重试次数进行重试。EnableRetryOnFailure方法的最后一个参数是用来设置错误代码的,只有设置了错误代码的错误,才会触发重试。获取错误代码的方法有很多种,个人比较推荐的是,通过异常信息进行获取,比如,使用MySql数据时,触发的异常类型是MySqlException,此类的Number属性的值EnableRetryOnFailure方法所需要的Number

2. DbContext线程问题

efcore不支持在同一个DbContext实例上运行多个并行操作,这包括异步查询的并行执行以及从多个线程进行的任何显式并发使用。因此,始终 await 异步调用,或对并行执行的操作使用单独的 DbContext 实例。

当 EF Core 检测到并行操作或多个线程同时尝试使用 DbContext 实例时,你将看到一条 InvalidOperationException,其中包含类似于下面的消息:

A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

意思是,在上一个操作没有执行完毕之前,又启动了一个新的操作,所以不能保证线程是安全的。

下面是一段错误的,可以触发这个异常的示例代码:

所以,请始终await异步调用。如果在多个多个线程中使用DbContext,需保证每个线程的DbContext的实例是唯一的。

3. 数据库使用连接池

使用 services.AddDbContextPool比使用 services.AddDbContext吞吐量提升在10~20的百分点(非官方说法,对性能提高数据是本人测试后得到的结果)。

需要注意的是,连接池大小并不是越大越好。

4. 日志记录

在使用ef时,基本上绝大多数和数据库的交互都是通过linq实现的,然后ef将linq翻译成对应的sql语句,在排查问题的时候,在开发或者排查问题时,往往需要关注最终执行的sql脚本,所以就需要通过日志的方式查看。

efcore2.x的版本默认是注入日志服务,所以不需要额外的操作,就可以查看对应的sql脚本。但efcore3.x的版本默认移除了日志服务,具体原因参照:https://docs.microsoft.com/zh-cn/ef/core/what-is-new/ef-core-3.0/breaking-changes#adddbc。

可通过自定义DbContext的方式注入日志任务,示例代码如下:

public static readonly ILoggerFactory MyLoggerFactory

= LoggerFactory.Create(builder => { builder.AddConsole(); });

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

{

base.OnConfiguring(optionsBuilder);

optionsBuilder.UseLoggerFactory(MyLoggerFactory);

}

当执行ef代码时,可在控制台中查看相关的sql脚本,如下图所示:

5. 增

插入数据到数据库常用的场景有:普通单表单行插入,多表级联插入,批量插入。

普通单表单行插入比较简单,实例代码如下:

var student = new Student {CreateTime = DateTime.Now, Name = "zjjjjjj"};

await _context.Students.AddAsync(student);

await _context.SaveChangesAsync();

多表级联插入,需要在实体映射中配置属性导航。

比如Blog表和Post是的关系是1对多的关系。则在Blog的实体中,定义一个类型为List 的属性。示例代码如下:

[Table("blog")]

public class Blog

{

[Column("id")]

public long Id { get; set; }

[Column("title")]

public string Title { get; set; }

public List<Post> Posts { get; set; }

[Column("create_date")]

public DateTime CreateDate { get; set; }

}

对应的插入语句如下所示:

var blog = new Blog

{

Title = "测试标题",

Posts = new List<Post>

{

new Post{Content = "评论1"},

new Post{Content = "评论2"},

new Post{Content = "评论3"},

}

};

await _context.Blog.AddAsync(blog);

await _context.SaveChangesAsync();

执行此代码,会生成如下的日志:

addafb15d97aa81e2ab51f412e2217e2.png

从日志中可以看出,通过这种方式实现了级联插入的效果。

批量插入实现方式有两种,一种是EF默认实现,适用于数据源较少的情况。另一种,我们基于EF开发一个大数据量批量插入的服务,适合于数据源大于1000的场景。在万级及以上的数据量上,较EF默认的批量插入性能上有非常明显的提升。具体参考:https://www.cnblogs.com/fulu/p/13370335.html

EF默认实现:

var list = new List<Student>();

for (int i = 0; i < num; i++)

{

list.Add(new Student { CreateTime = DateTime.Now, Name = "zjjjjjj" });

}

await _context.Students.AddRangeAsync(list);

await _context.SaveChangesAsync();

ISqlBulk实现:

var list = new List<Student>();

for (int i = 0; i < 100000; i++)

{

list.Add(new Student { CreateTime = DateTime.Now, Name = "zjjjjjj" });

}

await _bulk.InsertAsync(list);

自增 OR GUID

int自增的优点:

1、需要很小的数据存储空间,仅仅需要4 byte 。

2、insert和update操作时使用INT的性能比GUID好,所以使用int将会提高应用程序的性能。

3、index和Join 操作,int的性能最好。

4、容易记忆。

int自增的缺点:

1、使用INT数据范围有限制。如果存在大量的数据,可能会超出INT的取值范围。

2、很难处理分布式存储的数据表。

GUID做主键的优点:

1、唯一性。

2、适合大量数据中的插入和更新操作。

3、跨服务器数据合并非常方便。

GUID做主键的缺点:

1、存储空间大(16 byte),因此它将会占用更多的磁盘大小。

2、很难记忆。join操作性能比int要低。

3、没有内置的函数获取最新产生的guid主键。

4、EF默认生成的GUID是无序的,会影响数据插入性能。

结论:

在数据量比较少的场景下,建议使用int自增,比如分类。对于大数据量,建议使用有序GUID。因为默认.net生成GUID是无序的,而数据库中主键默认是聚集索引,而聚集索引在物理上的存储是有序的,当插入数据时,如果插入的是无序的GUID,可能就会涉及到移动数据的情况,进而影响插入的性能,特别是百万级数据量的时候,性能影响则较为明显。参考资料:https://www.cnblogs.com/CameronWu/p/guids-as-fast-primary-keys-under-multiple-database.html

其他可选方案:

经过个人多番了解,目前市面上常用的分布式id生成算法和Twitter发布的雪花算法大同小异,个人也在项目中使用过雪花算法,有兴趣的朋友可以在博客园找下相关的内容。不过目前用.net封装的雪花算法普遍较基础,很难在docker或者k8s环境下简单的使用,所以在此预告下,本人根据雪花算法编写的可用于k8s环境的即将开源,敬请期待。

6. 查

EF使用Linq查询数据库中的数据,使用Linq可编写强类型的查询。当命令执行时,EF先将Linq表达式转换成sql脚本,然后再提交给数据库执行。可在日志中查看生成的sql脚本。

根据条件查询:

await _context.Blog.Where(x=>x.Id>0).ToListAsync();

上述代码执行时生成的sql脚本如下所示:

SELECT `x`.`id`, `x`.`create_date`, `x`.`title`

FROM `blog` AS `x`

WHERE `x`.`id` > 0

获取单个实体

可实现获取单个实体的方式有First,FirstOrDefault,Single,SingleOrDefault

其中First,FirstOrDefault执行时生成的sql脚本如下:

SELECT `x`.`id`, `x`.`create_date`, `x`.`title`

FROM `blog` AS `x`

WHERE `x`.`id` > 10

LIMIT 1

Single,SingleOrDefault执行时生成的sql脚本如下:

SELECT `x`.`id`, `x`.`create_date`, `x`.`title`

FROM `blog` AS `x`

WHERE `x`.`id` > 10

LIMIT 2

细心的你应该已经发现了两者的区别,Single需要查询2条数据,当返回的数据多余一条时,Single,SingleOrDefault方法就会报Source sequence contains more than one element.异常。所以Single方法仅适用于查询条件对应的数据只有一条的场景,比如查询主键的值。如下所示:

await _context.Blog.SingleOrDefaultAsync(x => x.Id==100);

后缀带OrDefault和不带后缀的区别是,当sql脚本执行查询不到数据时,带后缀的会返回空值,而不带后缀的则会直接报异常。

判断数据库是否存在

可通过Any()和Count()方法实现是否存在数据。示例代码如下:

await _context.Blog.AnyAsync(x => x.Id > 100);

await _context.Blog.CountAsync(x => x.Id > 100)>0;

生成的sql脚本对应如下:

SELECT CASE

WHEN EXISTS (

SELECT 1

FROM `blog` AS `x`

WHERE `x`.`id` > 100)

THEN TRUE ELSE FALSE

END

SELECT COUNT(*)

FROM `blog` AS `x`

WHERE `x`.`id` > 100

乍一看,Any方法生成的脚本貌似更复杂些,但实际上,Any方法的性能在大数据量下比Count方法高了很多。所以在判断是否存在时,请使用Any方法。

连接查询

连接查询是关系数据库中最主要的查询,主要包括内连接、外连接(左连接、外连接)和交叉连接等。通过连接运算符可以实现多个表查询。本文主要讲解下常用的内连接和左连接。

内连接的示例代码如下:

var query = from post in _context.Post

join blog in _context.Blog on post.BlogId equals blog.Id

where blog.Id > 0

select new {blog, post};

左连接的示例代码如下:

var query = from post in _context.Post

join blog in _context.Blog on post.BlogId equals blog.Id

into pbs

from pb in pbs.DefaultIfEmpty()

where pb.Id>0 && post.Content.Contains("1")

select new {post,pb.Title};

级联查询

在很多场景中,可能会涉及到查询与父表关联的子表数据,在这样的场景中,会有一部分人先查出主表数据,然后根据主表的主键再去查询子表的数据,笔者在使用ef初期也是这种处理方式的。但借助Include的方法可以让我们更方便的解决父子表级联查询的问题。示例代码如下:

var result = await _context.Blog.Include(b => b.Posts) .SingleOrDefaultAsync(x=>x.Id==157);

如果有更多的层级,可以借助ThenInclude进行查询。

有的时候,还有这样的场景:我们不是简单的查询子表的数据,而是需要查询满足指定条件的数据,那就要求咱们在调用Include的方法时传入参数,示例代码如下:

var filteredBlogs = await _context.Blogs

.Include(blog => blog.Posts

.Where(post => post.BlogId == 1)

.OrderByDescending(post => post.Title)

.Take(5))

.ToListAsync();

注:以上方法仅在.net5中支持。所以,efcore也是在一个发展的过程中,随着时间与版本的更新,功能也会渐渐趋于完善。相关内容请参考:https://docs.microsoft.com/zh-cn/ef/core/querying/related-data

7. 改

使用过EF的应该都了解查询的跟踪与非跟踪的概念吧(纳尼?你没听说过,老衲给您指条明路吧:https://docs.microsoft.com/zh-cn/ef/core/querying/tracking)。

通常来讲,更新的流程大概是这样:查询出数据,修改某些字段的值,调用Update方法,然后调用SaveChange方法。看上去毫无破绽,但如果你仔细观察过生成的sql脚本的话,或许你就应该有更好的方法,咱们先来看看示例代码:

var school = await _context.Schools.FirstAsync(x => x.Id > 0);

school.Name = "6666";

_context.Schools.Update(school);

await _context.SaveChangesAsync();

如下图所示的是执行以上代码生成的update的sql语句,我们发现明明代码中只对Name重新赋了值,但生成的脚本却将此记录的所有字段进行了更新,显然这不是我们想要的结果。

3f0b8625038a429a8f37f635a4d497d3.png

其实,如果实体是通过跟踪查询得到的,则可直接调用SaveChage方法,而不用多余调用Update方法,此时,EF内部会自动判断哪些字段进行了更新,从而只生成值改变了的sql语句。

结论:当要更新的实体开启了跟踪,则更新时,无需调用Update方法, 直接调用SaveChange方法,此时之后更新值发生改变的字段。如果先调用Update则SaveChange,则不管实体的字段有没有更新,生成的sql脚本依旧会更新所有的字段,牺牲了性能。假如你的实体不是通过数据库的跟踪查询获取的,则在调用时才需要调用Update方法。


福禄ICH.架构出品

作者:福尔斯

2020年8月

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/573845.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos系统云服务器,Centos系统怎么进云服务器

Centos系统怎么进云服务器 内容精选换一换CentOS 7.5版本操作系统的弹性云服务器默认关闭SELinux功能。通过/etc/selinux/config开启SELinux功能后&#xff0c;在输入密码时&#xff0c;会出现无法登录的问题。如果业务需要开启SELinux 功能&#xff0c;请参照本节内容进行配置…

applicationproperties不是小叶子_为何“砂糖桔”是带着叶子出售?原来其中有着“猫腻”,涨知识了...

大家好&#xff0c;我是你们的好朋友童童&#xff0c;生活中不止有眼前的苟且&#xff0c;还有诗和梦想。童童每天都给大家分享有趣实用的小知识&#xff0c;有什么意见或者建议都可以和童童交流&#xff0c;喜欢我们的可以关注呦。一起和童童来看看今天的小知识吧。随着春节的…

用android制作一个记事本app_用扁平化呈现一个天气APP

作为当下最火的设计风格之一&#xff0c;扁平化设计其实是一种简约的Ul 设计理念&#xff0c;现被广泛应用于图形用户界面上&#xff0c;在图形材料&#xff0c;例如海报&#xff0c;艺术作品&#xff0c;指导文档&#xff0c;各类出版物等方面尤为常用。今天为大家带来了我最近…

hbase hdfs外部表_硬核干货长文!Hbase来了解一下不?

本文公众号来源&#xff1a;互联网侦察 作者&#xff1a;channingbreeze最近我也在入门大数据相关的基础&#xff0c;这篇文章非常通俗易懂讲解了什么是Hbase&#xff0c;推荐阅读&#xff01;等我学所成&#xff0c;我也来写写大数据相关的入门知识。今天&#xff0c;小史的…

springboot自动配置的原理_SpringBoot实战:详解SpringBoot自动配置原理

SpringBoot 自动配置主要通过 EnableAutoConfiguration, Conditional, EnableConfigurationProperties 或者 ConfigurationProperties 等几个注解来进行自动配置完成的。EnableAutoConfiguration 开启自动配置&#xff0c;主要作用就是调用 Spring-Core 包里的 loadFactoryName…

PYTHON之路(八)

http://www.cnblogs.com/alex3714/articles/5227251.htmlSocket语法及相关socket概念A network socket is an endpoint of a connection across a computer network. Today, most communication between computers is based on the Internet Protocol; therefore most network …

kafka消息消费有延迟_RabbitMQ与Kafka的技术差异以及使用注意点

导言作为一个有丰富经验的微服务系统架构师&#xff0c;经常有人问我&#xff0c;“应该选择RabbitMQ还是Kafka&#xff1f;”。基于某些原因&#xff0c; 许多开发者会把这两种技术当做等价的来看待。的确&#xff0c;在一些案例场景下选择RabbitMQ还是Kafka没什么差别&#x…

android 通知_Android 全局消息通知框架实现(类似EventBus)

Github项目地址 https://github.com/532268948/MessageDemo一、介绍随着页面的增多&#xff0c;页面之间的信息交流也会越来越多&#xff0c;比如一个社交类app,你在个人中心修改了自己的个人信息&#xff0c;在动态展示页面你就要通知该页面有关于自己的动态需要更新自己展示在…

2019运管取消了滴滴还查吗_劳务资质2019年取消吗?劳务资质新标准你真的了解吗?...

施工劳务资质2019新标准&#xff01;劳务资质已经取消&#xff1f;不可能&#xff01;虽然这样对于很多企业来说应该比较省事&#xff0c;劳务资质也是越来越受欢迎的&#xff0c;但是企业在办理资质之前一定要将资质标准了解清楚了&#xff0c;新版施工劳务资质仅仅是将专业进…

FFmpeg获取DirectShow设备数据(摄像头,录屏)

这两天研究了FFmpeg获取DirectShow设备数据的方法&#xff0c;在此简单记录一下以作备忘。本文所述的方法主要是对应Windows平台的。 1. 列设备 ffmpeg -list_devices true -f dshow -i dummy命令执行后输出的结果如下&#xff08;注&#xff1a;中文的设备会出现乱码的情…

矩阵的对数运算公式_必修一——对数与对数运算

一、前言(废话)高中数学我们已经学习了二次函数&#xff0c;指数函数(如果不记得的读者可以往前面翻看一下)&#xff0c;这次作者为读者们讲解的是对数与对数运算&#xff0c;对数是什么呢&#xff1f;读者们心里有自己的认知吗&#xff1f;二、对数对数函数是高中阶段学习的一…

分拣外观残缺的机器人_复合机器人AGV+协作机器人的应用领域

什么是复合机器人?复合型机器人是一种集成AGV移动机器人和通用工业机器人两项功能为一身的机器人。在工业领域&#xff0c;通用工业机器人被称为机械臂或者机械手&#xff0c;主要是替代人胳膊的抓取功能;而AGV移动机器人是替代人腿脚的行走功能。复合型机器人则是手脚并用&am…

前端之旅,做一点有回报的事情

本文是参加前端早读课【同说】做的分享 首先感谢情封对前端圈的辛苦付出&#xff0c;能坚持做好一件事真的很不容易&#xff0c;敬佩这份精神。2天前看到早读课的【同说】活动计划&#xff0c;我觉得想法真的很好&#xff0c;忽然情封微信邀请我参与&#xff0c;听到后我是心虚…

差异表达基因变化倍数_10.limma教程|差异表达统计相关理论

点击上方“蓝字”关注我们10.1 Top-Tables 简介limma包提供topTable和decideTests函数概括线性模型结果&#xff0c;进行假设检验&#xff0c;调整p值进行多重检验。结果包括(log2)倍数变化&#xff0c;标准误差&#xff0c;t统计和p值。用于显著性分析的基本统计量是修正t统计…

activemq中怎么知道推送消息是否成功_如何优雅的使用activeMQ 安装、应用、安全认证、持久化...

ActiveMQ安装1. 下载资源ActiveMQ官网&#xff1a;版本说明ActiveMQ5.10.x以上版本必须使用JDK1.8才能正常使用。ActiveMQ5.9.x及以下版本使用JDK1.7即可正常使用。上传至Linux服务器解压安装文件tar -zxf apache-activemq-5.9.0-bin.tar.gz检查权限ls -al apache-activemq-5.9…

DB2表结构DDL脚本导出

db2look是导出DDL语句脚本的命令&#xff0c;以下是对db2look的一个简单介绍。 语法&#xff1a;db2look -d <数据库名> -e -t <表名> -o <文件名>.dml -d&#xff1a;数据库名&#xff0c;这必须指定。 -e:抽取复制数据所需要的DDL文件&#xff0c;此项将生…

套装门安装_室内套装门-油漆工艺

上一期我们分享了套装门的分类和区别&#xff0c;主要从门芯和门皮的结构&#xff0c;主要分为纯实木门、实木复合门、模压门三种。也分享了三种类型的门的区别和基本定义&#xff0c;其中最为关键的是实木复合门&#xff0c;四层结构&#xff0c;其中门芯的填充料可操作空间很…

activiti 工作流_分享:springboot中关于工作流activiti的搭建

概念工作流产品使用activiti的算是比较多了&#xff0c;自带了一套UI界面&#xff0c;可以直接使用&#xff0c;用来设计流程&#xff0c;下面简单总结一下它的步骤&#xff1a;1 设计模型2 发布为流程&#xff0c;一个模型可以发布多个版本的流程3 建立一个流程的实例和实例任…

ae saber插件_AE激光插件Video Copilot Saber安装方法

AE激光Video Copilot Saber插件可以制作出能量光束&#xff0c;激光&#xff0c;传送门&#xff0c;闪电&#xff0c;电流&#xff0c;朦胧等特效&#xff0c;内含有40多种不同类型的特效预设&#xff0c; 下面说下AE激光插件saber安装方法。插件特色&#xff1a; 1.可以创建高…

继电器rc吸收电路取值_继电器的工作原理以及驱动电路解析

继电器是具有隔离功能的自动开关元件&#xff0c;广泛应用于遥控、遥测、通讯、自动控制、机电一体化及电力电了设备中&#xff0c;是最重要的控制元件之一。继电器实际上是用较小的电流去控制较大电流的一种“自动开关”。故在电路中起着自动调节、安全保护、转换电路等作用。…