ASP.NET Core 3.1系列(21)——EFCore中的更新实体操作

news2024/12/29 11:05:42

1、前言

前面的博客已经介绍过EFCore中关于新增和删除实体的相关操作,本文开始介绍EFCore中的更新实体操作。与新增实体和删除实体相比,更新实体的操作略微有些复杂,如果在代码的写法上不多加注意,那就很有可能会在后台生成效率低下的SQL,下面开始介绍。

2、构建测试数据库

还是与之前一样,在SQL Server中创建一个数据库Dao,然后创建一张Author数据表,代码如下:

USE [Dao]
GO

/****** Object:  Table [dbo].[Author]    Script Date: 2022/12/16 8:55:33 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Author](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](20) NULL,
	[Gender] [nvarchar](10) NULL,
	[Age] [int] NULL,
	[Email] [nvarchar](30) NULL,
 CONSTRAINT [PK_Author] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

Author表数据如下所示:

IdNameGenderAgeEmail
1张三3511111111@qq.com
2李四4022222222@qq.com
3王五3733333333@qq.com

最终生成的实体类和数据库上下文代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

// Code scaffolded by EF Core assumes nullable reference types (NRTs) are not used or disabled.
// If you have enabled NRTs for your project, then un-comment the following line:
// #nullable disable

namespace App.Models
{
    public partial class Author
    {
        [Key]
        public int Id { get; set; }
        [StringLength(20)]
        public string Name { get; set; }
        [StringLength(10)]
        public string Gender { get; set; }
        public int? Age { get; set; }
        [StringLength(30)]
        public string Email { get; set; }
    }
}
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using App.Models;

// Code scaffolded by EF Core assumes nullable reference types (NRTs) are not used or disabled.
// If you have enabled NRTs for your project, then un-comment the following line:
// #nullable disable

namespace App.Context
{
    public partial class DaoDbContext : DbContext
    {
        public DaoDbContext()
        {
        }

        public DaoDbContext(DbContextOptions<DaoDbContext> options)
            : base(options)
        {
        }

        public virtual DbSet<Author> Author { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer("Data Source=rt-dongshenfeng;Initial Catalog=Dao;User ID=sa;Password=gis1a6b7c!Z;");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}

3、更新实体——修改实体属性

一般可以通过修改实体属性的方式实现数据的更新,下面代码演示了将Name='张三'的实体年龄增加10岁:

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;
using System.Linq;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            Author author = _dbContext.Set<Author>().Where(p => p.Name == "张三").FirstOrDefault();
            if (author != null)
            {
                author.Age += 10;
                return _dbContext.SaveChanges();
            }
            return 0;
        }
    }
}

后台一共生成2SQL,可以发现update语句只对Age字段进行更新。

SELECT TOP(1) [a].[Id], [a].[Age], [a].[Email], [a].[Gender], [a].[Name]
FROM [Author] AS [a]
WHERE [a].[Name] = N'张三'
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Author] SET [Age] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

',N'@p1 int,@p0 int',@p1=1,@p0=45

如果希望实现批量更新,只需要在foreach循环中修改实体属性即可,下面代码演示了将Author表中的每条记录年龄增加10岁:

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            List<Author> authors = _dbContext.Set<Author>().ToList();
            foreach (Author author in authors)
            {
                author.Age += 10;
            }
            return _dbContext.SaveChanges();
        }
    }
}

4、更新实体——Update、UpdateRange

EFCore提供的UpdateUpdateRange方法也可以实现数据的更新,前者用于更新单个实体,后者可以实现批量更新实体,先来看一段示例代码:

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;
using System.Linq;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            Author author = _dbContext.Set<Author>().Where(p => p.Name == "张三").FirstOrDefault();
            if (author != null)
            {
                author.Age += 10;
                _dbContext.Update(author);
                return _dbContext.SaveChanges();
            }
            return 0;
        }
    }
}

与直接修改实体属性相比,这里调用了_dbContext.Update(author)强制更新,下面来看看后台生成的SQL

SELECT TOP(1) [a].[Id], [a].[Age], [a].[Email], [a].[Gender], [a].[Name]
FROM [Author] AS [a]
WHERE [a].[Name] = N'张三'
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Author] SET [Age] = @p0, [Email] = @p1, [Gender] = @p2, [Name] = @p3
WHERE [Id] = @p4;
SELECT @@ROWCOUNT;

',N'@p4 int,@p0 int,@p1 nvarchar(30),@p2 nvarchar(10),@p3 nvarchar(20)',@p4=1,@p0=55,@p1=N'11111111@qq.com',@p2=N'男',@p3=N'张三'

可以发现,后台的update语句不仅将Age字段更新了,同时也把其他字段都更新了。因此UpdateUpdateRange方法最好慎用,因为它们每次都会强制更新全部字段。这里你可能还有一个疑问:既然获取到实体后直接更新属性就能实现更新操作,那为什么还要提供这两个方法?其实上面的代码属于先查询、后更新,因此后台会生成2SQL,其中一条是select语句,另一条则是update语句。而UpdateUpdateRange方法允许你跳过查询阶段,直接创建实体实现数据更新,当然前提是你创建的实体必须包含主键,看下面一段代码:

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            Author author = new Author
            {
                Id = 1,
                Name = "张三",
                Age = 35
            };
            _dbContext.Update(author);
            return _dbContext.SaveChanges();
        }
    }
}

如果采用这种写法,后台只会生成1SQL

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Author] SET [Age] = @p0, [Email] = @p1, [Gender] = @p2, [Name] = @p3
WHERE [Id] = @p4;
SELECT @@ROWCOUNT;

',N'@p4 int,@p0 int,@p1 nvarchar(30),@p2 nvarchar(10),@p3 nvarchar(20)',@p4=1,@p0=35,@p1=NULL,@p2=NULL,@p3=N'张三'

查看一下运行结果,发现一个新的问题:GenderEmail字段变为NULL了?

在这里插入图片描述
在上面的代码中,我们创建的实体只显示声明了Id、Name、Age的字段值,而GenderEmail未显示声明,因此Update方法会默认将这两个字段赋值为NULL,解决方法也很简单,声明所有字段即可:

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            Author author = new Author
            {
                Id = 1,
                Name = "张三",
                Gender = "男",
                Age = 35,
                Email = "11111111@qq.com"
            };
            _dbContext.Update(author);
            return _dbContext.SaveChanges();
        }
    }
}

此时后台生成的SQL如下:

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Author] SET [Age] = @p0, [Email] = @p1, [Gender] = @p2, [Name] = @p3
WHERE [Id] = @p4;
SELECT @@ROWCOUNT;

',N'@p4 int,@p0 int,@p1 nvarchar(30),@p2 nvarchar(10),@p3 nvarchar(20)',@p4=1,@p0=35,@p1=N'11111111@qq.com',@p2=N'男',@p3=N'张三'

5、更新实体——Entry

Entry方法与Update方法类似,它会对全部字段进行更新,来看下面一段代码:

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            Author author = _dbContext.Set<Author>().Where(p => p.Name == "张三").FirstOrDefault();
            if (author != null)
            {
                author.Age += 10;
                _dbContext.Entry(author).State = EntityState.Modified;
                return _dbContext.SaveChanges();
            }
            return 0;
        }
    }
}

后台共生成2SQL

SELECT TOP(1) [a].[Id], [a].[Age], [a].[Email], [a].[Gender], [a].[Name]
FROM [Author] AS [a]
WHERE [a].[Name] = N'张三'
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Author] SET [Age] = @p0, [Email] = @p1, [Gender] = @p2, [Name] = @p3
WHERE [Id] = @p4;
SELECT @@ROWCOUNT;

',N'@p4 int,@p0 int,@p1 nvarchar(30),@p2 nvarchar(10),@p3 nvarchar(20)',@p4=1,@p0=45,@p1=N'11111111@qq.com',@p2=N'男',@p3=N'张三'

Update类似,Entry也支持直接创建实体更新的操作,但前提是实体必须包含主键,同时创建实体时未被显示赋值的字段将会被更新为NULL

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            Author author = new Author
            {
                Id = 1,
                Name = "张三",
                Gender = "男",
                Age = 35
            };
            _dbContext.Entry(author).State = EntityState.Modified;
            return _dbContext.SaveChanges();
        }
    }
}

后台生成的SQL如下,由于上面的代码未对Email字段进行显示赋值,因此该字段被更新为NULL

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Author] SET [Age] = @p0, [Email] = @p1, [Gender] = @p2, [Name] = @p3
WHERE [Id] = @p4;
SELECT @@ROWCOUNT;

',N'@p4 int,@p0 int,@p1 nvarchar(30),@p2 nvarchar(10),@p3 nvarchar(20)',@p4=1,@p0=35,@p1=NULL,@p2=N'男',@p3=N'张三'

在这里插入图片描述
如果只希望执行一次SQL,又不希望显示对每个字段都进行赋值,那么可以使用Property方法结合Entry实现部分字段的更新操作,代码如下:

using App.Context;
using App.Models;
using Microsoft.AspNetCore.Mvc;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorController : ControllerBase
    {
        protected readonly DaoDbContext _dbContext;

        public AuthorController(DaoDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        [HttpGet]
        public ActionResult<int> Update()
        {
            Author author = new Author
            {
                Id = 1,
                Gender = "女"
            };
            _dbContext.Entry(author).Property("Gender").IsModified = true;
            return _dbContext.SaveChanges();
        }
    }
}

后台生成的SQL如下所示:

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Author] SET [Gender] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

',N'@p1 int,@p0 nvarchar(10)',@p1=1,@p0=N'女'

6、结语

EFCore中的更新操作略微有些复杂,正确理解每种更新操作生成的SQL是把控程序效率的关键。

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

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

相关文章

利用空余时间成为“业余”的自动驾驶的开发者

作为一名开发者&#xff0c;我时常会阅读一些相关的技术杂志和周刊&#xff0c;了解一些近期比较热门的技术和事件&#xff0c;要说现在技术领域最有发展前景的方向之一&#xff0c;很多人会想到自动驾驶。但现在国内做自动驾驶平台的并不多&#xff0c;其中百度做得是相对比较…

【Web】浅谈Http的请求方式和数据请求格式ContentType

我本来Http的请求方式和数据请求格式是大家开发过程中都默认知道的事情&#xff0c;直到我发现我的前端竟然不知道表单请求的时候&#xff0c;我觉得我有必要跟大家一起来讨论一下这个话题了。有可能我的前端小伙伴在学习的时候一开始就入手现在比较流行的前端框架如Vue、React…

3dmax 建模插件 Rappa Tools 3 笔记

1功能概述&#xff1a; RappaTools3是一个高级工具箱&#xff0c;为在3ds Max中工作的艺术家提供了各种各样的工具。主要的重点是加快工作流程和减少点击量。它提供了各种各样的工具&#xff0c;从选择工具到渲染工具。它可以帮助您完成创建3D艺术作品的整个过程。 它带有3个…

C#,图像二值化(05)——全局阈值的联高自适应算法及其源代码

阈值的选择当然希望智能、简单一些。应该能应付一般的图片。 What is Binarization? Binarization is the process of transforming data features of any entity into vectors of binary numbers to make classifier algorithms more efficient. In a simple example, trans…

优思学院|怎么把DPMO/不良率换算成六西格玛水平?

如何计算西格玛水平&#xff1f; 为了更形像化地说明西格玛水平&#xff08;Sigma Level&#xff09;&#xff0c;我们设定一个场景作为例子&#xff0c;假设你是一家电力公司&#xff0c;你会如何评估你公司的质量水平呢&#xff1f;你可能会以电网供电时的正常运行时间来衡量…

知识付费海哥:知识变现三剑客

小时候看武侠戏&#xff0c;那些古代剑客&#xff0c;飞檐走壁&#xff0c;神出鬼没&#xff0c;行走江湖&#xff0c;行侠仗义&#xff0c;牛逼的很&#xff01;心里贼仰慕&#xff01; 记忆最深的&#xff0c;是剑客们稳、准、狠的绝技剑法&#xff0c;剑起头落&#xff0c;一…

python实现字幕雨效果实现

先看最终实现的效果图&#xff1a; 使用python实现以上字幕雨效果&#xff0c;用到的主要库是pygame&#xff1b; pygame不是内置模块&#xff0c;需要先安装一下&#xff1a; 安装pygame 安装方式推荐有很多种&#xff0c;推荐使用pip&#xff1b; pip 是 Python 的包安装程…

RabbitMQ 订阅模型-路由模式

订阅模型-路由模式&#xff0c;此时生产者发送消息时需要指定 RoutingKey&#xff0c;即路由 Key&#xff0c;Exchange 接收到消息时转发到与 RoutingKey 相匹配的队列中。 在 Direct 模型下&#xff1a; 队列与交换机绑定&#xff0c;不能任意绑定&#xff0c;而要指定一个 Ro…

线上贷款申请违约风险预测大数据模型

通过模型可以得出模型分类准确率&#xff0c;通过客户信用违约风险预测模型&#xff0c;可以实现多渠道预警&#xff0c;形成多角度观察、多方面分析、多渠道传递的工作局面。

Python每日一练 10——for循环

Python每日一练 10——for循环 文章目录Python每日一练 10——for循环一、for循环介绍二、实例一&#xff1a;等差数列前n项和三、实例二&#xff1a;计算阶乘四、实例三&#xff1a;拉马努金法计算圆周率五、实例四&#xff1a;百钱买百鸡一、for循环介绍 for循环一般用于循环…

前端_Vue_9.模板引用、组件基础

文章目录一、模板引用1.1. 访问模板引用1.2. v-for 中的模板引用1.3. 函数模板引用1.4. 组件上的 ref1.5. 小结二、组件基础 ⭐2.1. 定义一个组件2.2. 使用组件2.3. 传递props2.4. 监听事件2.5. 通过插槽&#xff08;slot&#xff09;来分配内容2.6. 动态组件2.7. DOM模板解析注…

go 库 Cobra 现代化的命令行框架

go 库 Cobra 现代化的命令行框架 文章目录go 库 Cobra 现代化的命令行框架1. 简介2. 主要功能3. 应用举例4. Cobra 安装5. 使用 Cobra 库创建命令5.1 创建 rootCmd5.2 创建 main.go5.3 添加命令5.4 编译并运行6. 特性6.1 使用标志6.2 非选项参数验证6.3 PreRun and PostRun Hoo…

手绘图说电子元器件-电声转换器件

电声转换器件包括能够将电信号转换为声音的扬声器、耳机、讯响器和蜂鸣器,能够将声音转换为电信号的传声器,能够进行电磁转换的磁头和具有压电效应的晶体等。 扬声器 扬声器俗称喇叭,是一种常用的电声转换器件,其基本作用是将电信号转换为声音,在收音机、录音机、电视机…

Linux | 套接字(socket)编程 | TCP协议讲解 | 通信模型搭建

文章目录TCP模型的特性TCP接口介绍TCP服务器套接字设置TCP客户端套接字设置TCP模型的特性 TCP是属于传输层协议的一种&#xff0c;上篇博客介绍了另一种传输层协议——UDP&#xff0c;关于它们之间的区别&#xff0c;这里再提一下 TCPUDP传输层协议传输层协议有连接无连接可靠…

Word控件Spire.Doc 【评论】教程(3):在C#、VB.NET中从Word文档中提取注释并保存在TXT文件中

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

[Leetcode] 合并两个有序数组、链表

1.合并两个有序数组 原地合并数组&#xff0c;即不使用额外的空间 --> 使用三个指针&#xff0c;从尾部往前处理 题目链接&#xff1a;https://leetcode.cn/problems/merge-sorted-array/ nums1 总长度 mn&#xff0c;自身长度m&#xff1b;nums2 自身长度n&#xff0c; 使…

SSRF渗透与攻防(一)

目录 前言 SSRF是什么 危害&#xff08;利用方式): SSRF漏洞原理&#xff1a; CURL协议&#xff1a; SSRF常见场景 社会化分享功能&#xff1a; 如何发现SSRF漏洞 工具利用&#xff1a; 如何防御SSRF漏洞 前言 SSRF(Server-Side Request Forgery:服务器端请求伪造) 是…

rocketmq 实战问题汇总

rocketmq 实战过程会遇到这样或者那样的问题&#xff0c;今天我们专门抽出一篇文章来分析一下汇总一下&#xff0c;避免以后踩同样的坑&#xff1a; 1、找不到JDK的问题&#xff1a; 综合分析&#xff0c;是因为JDK安装的目录有空格导致的&#xff1a;Program Files 两个单词之…

电子招标采购系统源码—企业战略布局下的采购寻源

​ 智慧寻源 多策略、多场景寻源&#xff0c;多种看板让寻源过程全程可监控&#xff0c;根据不同采购场景&#xff0c;采取不同寻源策略&#xff0c; 实现采购寻源线上化管控&#xff1b;同时支持公域和私域寻源。 询价比价 全程线上询比价&#xff0c;信息公开透明&#xff0…

CANoe—基于DoIP通过CAPL实现与ECU通信测试

如下连接是在CANoe中基于DoIP通过加载诊断数据库实现CANoe与待测ECU诊断通信: CANoe链接 本文继续此话题,通过一个简单的CAPL Demo,实现CANoe与ECU进行DoIP通信。 首先在CANoe新建Ethernet工程: 在CANoe “Simulation Setup”中新建CAPL Test Module: 在此例中采用CANo…