查缺补漏系统学习 EF Core 6 - 修改实体数据

推荐关注「码侠江湖」星标,时刻不忘江湖事

这是 EF Core 系列的第六篇文章,上一篇文章讲述了 EF Core 中的原始 SQL 语句查询。

这篇文章讲一讲 EF Core 如何修改实体数据。

点击上方或后方蓝字,阅读 EF Core 系列合集。

b9afbd3d71fb0a836958986cd73cebcb.png

实体状态

在开始学习 EF Core 修改数据之前,我们必须要熟悉 EF Core 中的一些机制。

前面的内容中,我们讲到 DbContext 包括三个属性:ChangeTrackerDatabaseModel

之前的示例中,DatabaseModel 我们都有用到过,那么 ChangeTracker 是做什么的?

ChangeTracker 属性提供了对当前加载的实体的,变化跟踪信息和操作的访问。

当我们想执行任何数据库修改操作时,无论是创建、修改还是删除一个实体,EF Core 都有关于跟踪和操作的信息。

为什么要保存这些信息呢?

因为在 EF Core 中,修改实体属性的值,并不会直接被保存到数据库中。

而是要我们调用 SaveChanges 方法,EF Core 才会将修改反应到数据库,在此之前,EF Core 不会执行任何操作。

因此,在调用 SaveChanges 方法之前, EF Core 需要知道我们都执行了什么操作,这对 EF Core 来说非常重要。

每一个被追踪的实体,都有附属于它的 State 属性。

当我们使用上下文对象加载实体,而不使用 AsNoTracking 方法时,或者我们通过 UpdateRemoveAdd 方法,改变实体状态时,该实体都会成为被跟踪的实体。

状态属性的值可以通实体的 State 方法获得。

那么实体的状态有哪些呢?

  • 「Detached」 - 该实体没有被追踪,调用 SaveChanges 方法不会有任何效果

  • 「Unchanged」 - 该实体从数据库中加载,但没有任何变化。调用 SaveChanges 方法也不会有任何效果

  • 「Added」 - 该实体不存在于数据库中,调用 SaveChanges 方法会将其添加到数据库中。

  • 「Modified」 - 该实体存在于数据库中,并被修改过,因此,调用 SaveChanges 方法将在数据库中修改它

  • 「Deleted」 - 该实体存在于数据库中,调用 SaveChanges 方法,会将它从数据库中删除。

实体操作

接下来,让我们结合示例来演示增删改,并观察实体状态的变化:

var account = new Account
{Name = "张三",Age = 18
};
_context.Add(account);
_context.SaveChanges();

示例中,首先创建了一个 account 对象,此时它还未被附加到 EF Core 上下文中,它的状态应该是 Detached

当我们使用 Add 方法,将account对象作为实体添加到上下文后,它的状态应该是 Added

紧接着,使用 SaveChanges 方法,将新添加的实体,保存到数据库中,它的状态应该是 Unchanged

当我们再次修改实体中的任意属性时,它的状态会变成 Modified

// ...
account.Age = 20;
_context.SaveChanges();

使用 SaveChanges 方法,将已修改的实体,应用到数据库时,它的状态又会变回 Unchanged

最后,当我们从上下文中删除这个实体后,实体的状态会变成 Deleted

// ...
_context.Remove(account);
_context.SaveChanges();

再次使用 SaveChanges 方法,将实体从数据库中彻底删除,大家想一想,此时它的状态依然会变成什么?

这次可不是 Unchanged 了,因为它已经从上下文中被移除了,所以它的状态是 Detached

EF Core 正是用这么几个方法,对实体进行增删改。

当然,这只是其中一种方式。

在这个示例中,新增的对象经过 Add 方法,被添加到了上下文。

之后的更新和删除操作,都是针对实体已经存在于上下文中的情况。

那么如果,我们有一个对象,它不存在于上下文中,但它确实存在于数据库中,我们该如何对它就行更新或删除呢?

首先,你肯定不能用 Add 方法,虽然它可以将一个对象,作为实体添加到上下文中

但它还会使实体状态成为 Added,而不是 Modified,这种情况下执行 SaveChanges 方法,EF Core 只会生成插入数据的语句,肯定会造成数据库冲突,因为数据已存在。

比如,这个对象:

var account = new Account
{Id = new Guid("dd4feb0b-b57c-4338-a40a-7aa73fc6e460"),Name = "Zilor",Age = 99
};

实际开发中,它可能来自的客户端,这个对象的数据是经过之前的查询得到的。

现在客户端对它的属性进行了修改,主键肯定是不会变的。

此时,它的状态是 Detached,没有被附加到上下文中,我们该如何直接更新到数据库呢?

这里有两种方法可以做到:

一是先用 ID 从 EF Core 中查询出实体,然后用 account 对象中的属性值,修改实体中的值,再使用 SaveChanges 保存数据。

比如这样:

var dbAccount = _context.Accounts.FirstOrDefault(a => a.Id == account.Id);
dbAccount.Name = account.Name;
dbAccount.Age = account.Age;
_context.SaveChanges();

需要注意的是,如果修改的操作,没有造成实体值的任何变化,实体状态将仍是 Unchanged

此时,即便执行SaveChanges方法, 也不会有任何效果。

二是用附加方法,将 account 对象附加到上下文,然后手动修改它的状态,再使用 SaveChanges 保存数据。

比如这样:

var account = new Account
{Id = new Guid("dd4feb0b-b57c-4338-a40a-7aa73fc6e460"),Name = "Zilor",Age = 99
};_context.Accounts.Attach(account);
_context.Entry(account).State = EntityState.Modified;
_context.SaveChanges();

Attach 方法用来附加实体,被附加的实体初始状态为 Unchanged,所以我们要手动修改实体状态。

执行应用,可以看到实体状态的流转。

第二种方式相对第一种方式,少了一步查询,也少了实体属性的赋值。

但是,无论实体的值相对于数据库是否有变化,更新操作都会执行。

这是方式适合全量更新,因为如果客户端传来的是不完整的对象,只包含了修改的属性,那么更新时可能会造成问题。

比如这个例子,如果 Account 对象中没有 Age 属性,更新到数据库时,Age 列的值会是个 「Null」

第一种方式,由于是从实体查询出来的数据,数据是完整的,修改也只是针对个别属性,所以保存数据时,不会发生这种情况。

而且第一种方式,可以判断出实体是否真的已经修改,无修改的话,不会执行任何操作。

所以,对于更新操作,建议使用第一种方式,更安全也更靠谱;

删除操作也是如此,只需要将实体状态修改为 Deleted,就可以直接删除,不够删除操作可以使用第二种方式。

更多精彩内容,请关注我▼▼

c86db9f4bb0281dee67532f2e4f97ec6.gif

如果喜欢我的文章,那么

在看和转发是对我最大的支持!

(戳下面蓝字阅读)

e4390b71d0dd332557a03d3060e4db56.png

推荐关注微信公众号:码侠江湖

                        a7606bd73dcef202e659b3be38047415.png觉得不错,点个在看再走哟

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

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

相关文章

在AngularJS的controller外部直接获取$scope

为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处。LaplaceDemon/SJQ。 http://www.cnblogs.com/shijiaqi1066/p/5560843.html 以前利用webqq的写过一个自动发消息的脚本,由于那时webqq还直接使用类似jQuery操作DOM的技术&#x…

js pug 代码_pug模版学习(一)

标签按照html的缩进格式doctype htmlhtmlheadtitlebody编译结果:文本p 这是文本| 这是文本p.这是文本编译结果:这是文本这是文本这是文本属性设置class名跟id名(默认是div)p.foop#foop#foo.foo.foo#foo编译结果:其他属性:a(href&q…

Upgrade Hole puncher Mathematical Modeling

// AntColony.cpp : 定义控制台应用程序的入口点。 //#include<iostream> #include<math.h> #include<time.h> #include<stdio.h> #include <fstream> #include <string> #include <iostream> #include <vector>using n…

Android之提示Cannot call this method while RecyclerView is computing a layout or scrolling

1 问题 java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling androidx.recyclerview.widget.RecyclerView{24d6f3b VFED.V... ......ID 0,657-1074,1911 #7f090143 app:id/recyclerView}, adapter:com.appsinno…

Java8新的异步编程方式 CompletableFuture(三)

前面两篇文章已经整理了CompletableFuture大部分的特性&#xff0c;本文会整理完CompletableFuture余下的特性&#xff0c;以及将它跟RxJava进行比较。 3.6 Either Either 表示的是两个CompletableFuture&#xff0c;当其中任意一个CompletableFuture计算完成的时候就会执行。 …

情人节,我表白了CSDN小姐姐后,竟然...【为表白写了一个绘图工具,让我不再手残】

情人节&#xff0c;我表白了CSDN小姐姐后&#xff0c;竟然…竟然有人看了这篇文。 以下图片素材由一个还没写完的工具绘制&#xff0c;稍后会放在CSDN的代码仓库&#xff08;现在能用了&#xff0c;还没时间改&#xff0c;颜色填充算法还没写&#xff0c;有能力的朋友可以修改一…

【小程序】刘一哥课堂随机点名提问神器V1.0(附源程序)

为了能让我们的孩子们尽量来教室上课,增强课堂的参与度,激发课堂激情,提高学习效率,一哥也是煞费苦心,于是开发出了这么一款课堂点名提问神器,跟大家分享一下。 打开神器,看到的界面是这样子的,我很感激有勇气按时起床并能到教室的每一位有志之士。 点击【开始】按钮,…

org.hibernate.HibernateException: No Session found for current thread

spring、springmvc和hibernate整合 在sessionFactory.getCurrentSession()时&#xff0c;出现以下异常 No Session found for current thread但使用sessionFactory.openSession()是没有任何问题的 严重: Servlet.service() for servlet [springDispatcherServlet] in context w…

java mysbatis select_MyBatis SELECT基本查询实现方法详解

1、返回一个LISTselect * from tbl_employee where last_name like #{lastName}2、将查询记录封装为一个Mapselect * from tbl_employee where id#{id}返回一条记录的map&#xff1b;key就是列名&#xff0c;值就是对应的值。3、多条记录封装为一个mapMapKey("id")pu…

Git之怎么通过命令修改前面几次提交的记录

1 问题 我们平时用gitlab,github发现提交代码上库记录写错了&#xff0c;需要修改回来。 2 解决办法

Git客户端TortoiseGit(Windows系统)的使用方法

本文环境&#xff1a; 操作系统&#xff1a;Windows XP SP3 Git客户端&#xff1a;TortoiseGit-1.8.8.0-32bit 一、安装Git客户端 全部安装均采用默认&#xff01; 1. 安装支撑软件 msysgit: https://code.google.com/p/msysgit/downloads/list?qfullinstallerofficialgit 当前…

.Net 在容器中操作宿主机

1方案描述 在 docker 容器中想操作宿主机&#xff0c;一般会使用 ssh 的方式&#xff0c;然后 .Net 通过执行远程 ssh 指令来操作宿主机。本文将使用 交互式 .Net 容器版 中提供的镜像演示 .Net 在容器中如何操作宿主机。2前期准备 1. 宿主机上生成 ssh key生成 ss…

【看动漫学编程】程序员在异世界生个娃 第1篇:太极村

前言 作者文笔比较水&#xff0c;还请见谅。 以下内容还将使用视频动态漫画表现&#xff0c;剪辑完将会贴出链接。 小说剧情为剧情需要&#xff0c;过渡到知识点&#xff0c;部分篇幅可能没有技术知识点还望谅解。 由于没有经费支持&#xff0c;所以画出来的东西是我自己用代码…

【ArcGIS风暴】最牛逼空间数据批处理神器来了:用户自定义工具箱GeoStorm.tbx

【Warming up】在学习和工作的过程中,作者曾写过很多采用ArcGIS模型构建器(Model Builder)、Python代码等批处理方法(感兴趣的GISers可以去【测绘地理信息Big风暴专】栏去交流学习指导),大大的减轻了操作压力,提高了工作效率。今天给大家展示一款神器:自定义工具箱GeoS…

2.6. PostgreSQL表之间连接

到目前为止&#xff0c;我们的查询一次只访问了一个表。查询可以一次访问多个表&#xff0c;或者用某种方式访问一个表&#xff0c;而同时处理该表的多个行。一个同时访问同一个或者不同表的多个行的查询叫连接查询。举例来说&#xff0c;比如你想列出所有天气记录以及这些记录…

Android之Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains

1 问题 用takePhoto去照相的时候特么的一打开就报这个错误 2020-04-09 21:33:49.124 19016-19016/com.appsinnova.android.keepshare E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.appsinnova.android.keepshare, PID: 19016java.lang.RuntimeException: Unable to …

Linux下c/c++项目代码覆盖率的产生方法

最近做了一系列的单元测试相关的工作&#xff0c;除了各种规范及测试框架以外&#xff0c;讨论比较多的就是关于代码覆盖率的产生&#xff0c;c/c与其他的一些高级语言或者脚本语言相比较而言&#xff0c;例如 Java、.Net和php/python/perl/shell等&#xff0c;由于没有这些高级…

C# WPF从后台代码生成行列可变的表格

z概述WPF常用的表格控件是DataGrid&#xff0c;这个控件在前台XAML编写的话&#xff0c;一般列已经固定&#xff0c;然后给每个列去绑定数据&#xff0c;但是如果我的列不固定&#xff0c;随着运算结果变动呢&#xff1f;这时候DataGrid&#xff0c;就比较难实现这个需求&#…

软件架构实践文章链接

2019独角兽企业重金招聘Python工程师标准>>> 架构 InfoQ: 又拍网架构中的分库设计 SNS网站数据库技术分析 - 51CTO.COM 数据库水平切分的实现原理解析 - iBATIS - Java - JavaEye论坛 基于amoeba的mysql分布式数据库学习&#xff08;一&#xff09; - Java - JavaEy…

【看动漫学编程】程序员在异世界生个娃 第2篇:外挂已准备就绪

前言 作者文笔比较水&#xff0c;还请见谅。 以下内容还将使用视频动态漫画表现&#xff0c;剪辑完将会贴出链接。 小说剧情为剧情需要&#xff0c;过渡到知识点&#xff0c;部分篇幅可能没有技术知识点还望谅解。 由于没有经费支持&#xff0c;所以画出来的东西是我自己用代码…