EFCore之增删改查
1. 连接数据库
通过依赖注入配置应用程序,通过startup类的ConfigureService方法中的AddDbContext将EFCore添加到依赖注入容器
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<OpenDbContext>(
options => options.UseMySql(Configuration["DbConfig:Mysql:ConnectionString"]);
}
将名为 OpenDbContext的 DbContext 子类注册到依赖注入容器的Scope生命周期。上下文配置为使用MySQL数据库提供程序,并从配置中读取数据库连接字符串。
OpenDbContext类必须公开具有 DbContextOptions 参数的公共构造函数。 这是将 AddDbContext 的上下文配置传递到 DbContext 的方式。
public class OpenDbContext : DbContext
{
public OpenDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Score> Scores { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//另一种配置连接数据库的方式
//optionsBuilder.UseMySql("连接数据库", ServerVersion.AutoDetect("连接数据库字符串"));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//属性配置
//modelBuilder.Entity<User>().Property(t => t.Account).IsRequired().HasMaxLength(20).HasComment("帐号");
//种子数据设置
//modelBuilder.Entity<User>().HasData(new User { Account="种子"});
//使用下面的方法进行替换处理上面批量增加etc的操作
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
base.OnModelCreating(modelBuilder);
}
}
然后将OpenDbContext通过构造函数注入的方式注入到应用程序的控制器或者其他服务中使用。
关于连接数据库可以参考另一个文章: .Net之生成数据库全流程
2. 操作数据库
context.Database.EnsureDeleted();//删除数据库,如果存在,如果没有权限,则引发异常
context.Database.EnsureCreated();//如果数据库不存在,创建数据库并初始化数据库架构,如果存在任何表,则不会初始化架构
context.Database.Migrate();//根据迁移文件,迁移数据库
3. 查询操作
3.1 基础查询
db.Set<UserInfor>().ToList();
//查询表达式
var account = (from u in _context.Users
where u.Id == id
select u.Account
).ToList();
//查询单个
_context.Movie.FirstOrDefaultAsync(m => m.ID == id);
_context.Movie.FindAsync(id);
//查询指定列
_context.Set<User>().AsNoTracking().Where(t=>t.Id=="11").Select(t => new { t.Account, t.PassWord }).FirstOrDefaultAsync();
// 预先加载查询
var blogs = context.Blogs.Include(blog => blog.Posts).ToList();
// 包含多个层级的查询
var blogs = context.Blogs.Include(blog => blog.Posts).ThenInclude(post => post.Author).ToList();
3.2 跟踪和非跟踪查询
跟踪行为决定了EFCore是否将有些实体的信息保存在其更改更跟踪器中。如果已跟踪某个实体,则该实体中检测到的任何更改都会在SaveChanges()时候保存到数据库,
不跟踪没有主键的实体类型。
# 跟踪查询
_context.Set<User>().ToListAsync();
# 非跟踪查询
_context.Set<User>().AsNoTracking().ToListAsync();
默认是跟踪查询
3.3 条件查询
3.3.1 不支持异步方案
Func<User, bool> express = x => true;
if (!string.IsNullOrWhiteSpace(dto.Data))
{
express = x => x.Mobile == dto.Data;
}
string userid = "";
if (!string.IsNullOrWhiteSpace(userid))
{
express = x => x.UserId == userid;
}
var bbb = _dbContext.Set<User>().Where(express).FirstOrDefault();
3.3.2 支持异步方案
Expression<Func<User, bool>> express = x => true;
if (!string.IsNullOrWhiteSpace(dto.Data))
{
express = x => x.Mobile == dto.Data;
}
var bbb = await _dbContext.Set<User>().Where(express).ToListAsync();
3.4 原生SQL查询
可使用 FromSqlRaw 扩展方法基于原始 SQL 查询开始 LINQ 查询。 FromSqlRaw 只能在直接位于 DbSet<> 上的查询根上使用。
3.4.1 基本原生SQL查询
var blogs = context.Blogs
.FromSqlRaw("select * from user")
.ToList();
// 执行存储过程
var blogs = context.Blogs
.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogs")
.ToList();
3.4.2 参数化查询
3.4.2.1 SQL注入
首先我们编写一个简单的SQL注入示例,比如就注入我们根据ID查询的语句,输入ID为:ididid' or '1'='1
var strSql = string.Format("select * from user where Id='{0}'", "ididid' or '1'='1");
var query = await _context.Set<User>().FromSqlRaw(strSql).ToListAsync();
Console.WriteLine(JsonConvert.SerializeObject(query));
生成语句
select * from user where Id='ididid' or '1'='1'
[{"Account":"张三","PassWord":"123456","CreateTime":"2021-05-20T22:53:44.778101","IsValid":false,"Id":"1395392302788120576"},{"Account":"李四","PassWord":"123456","CreateTime":"2021-05-20T22:53:44.849376","IsValid":false,"Id":"1395392303090110464"},{"Account":"王五","PassWord":"123456","CreateTime":"2021-05-20T22:53:44.849425","IsValid":false,"Id":"1395392303090110467"}]
3.4.2.2 FromSqlRaw参数化
通过参数化查询,防止SQL注入问题
//sql语句参数化查询,防止SQL注入
var strSql = "select * from user where Id=@id";
var parameter = new MySqlParameter[] {
new MySqlParameter("@id","1395392302788120576"),
};
var query = await _context.Set<User>().FromSqlRaw(strSql, parameter).ToListAsync();
或者
var strSql = "select * from user where Id={0}";
var query = await _context.Set<User>().FromSqlRaw(strSql, "1395392302788120576").ToListAsync();
Console.WriteLine(JsonConvert.SerializeObject(query));
// 生成SQL
select * from user where Id=@p0
[{"Account":"张三","PassWord":"123456","CreateTime":"2021-05-20T22:53:44.778101","IsValid":false,"Id":"1395392302788120576"}]
通过占位符形式提供额外的参数,看上去类似于String.Format语法,但是提供的值包装在DbParameter中。可以防止SQL注入
3.4.2.3 FromSqlInterpolated参数化
FromSqlInterpolated 类似于 FromSqlRaw,但你可以借助它使用字符串内插语法。 与 FromSqlRaw 一样,FromSqlInterpolated 只能在查询根上使用,并且都可以防止SQL注入。
var query = await _context.Set<User>().FromSqlInterpolated($"select * from user where Id={"1395392302788120576"}").ToListAsync();
Console.WriteLine(JsonConvert.SerializeObject(query));
生成SQL
select * from user where Id=@p0
[{"Account":"张三","PassWord":"123456","CreateTime":"2021-05-20T22:53:44.778101","IsValid":false,"Id":"1395392302788120576"}]
3.4.3 限制
- SQL查询必须返回实体类型的所有属性的数据。
- 结果集中的列明必须与属性映射到的列名称匹配。
- SQL查询不能包含关联数据, 但是,在许多情况下你可以在查询后面紧跟着使用
Include
方法以返回关联数据(请参阅包含关联数据)。
参考文档:https://docs.microsoft.com/zh-cn/ef/core/querying/raw-sql
3.5 复杂查询
数据如下:
用户表(user)
用户成绩表(score)
描述:包含三个用户,其中两个用户在成绩表都有语文和数学的数据。
3.5.1 内连接
内连接:分为隐式内连接和显式内连接(写法不同,结果相同)
3.5.1.1 Linq查询表达式
显式内连接:join-in-on拼接
var list = (from u in _context.Users
join sc in _context.Scores on u.Id equals sc.UserId
where sc.CourseName == "语文"
select new
{
u.Account,
u.PassWord,
sc.CourseName,
sc.Grade
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(list));
记得引用:System.Linq 否则提示:未找到源类型“DbSet”的查询模式的实现,未找到join
生成SQL
SELECT `u`.`Account`, `u`.`PassWord`, `s`.`CourseName`, `s`.`Grade`
FROM `user` AS `u`
INNER JOIN `score` AS `s` ON `u`.`Id` = `s`.`UserId`
WHERE `s`.`CourseName` = '语文'
结果
隐式内连接:多个from并联拼接
var list = (from u in _context.Users
from sc in _context.Scores
where u.Id == sc.UserId && sc.CourseName == "语文"
select new
{
u.Account,
u.PassWord,
sc.CourseName,
sc.Grade
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(list));
生成SQL
SELECT `u`.`Account`, `u`.`PassWord`, `s`.`CourseName`, `s`.`Grade`
FROM `user` AS `u`
CROSS JOIN `score` AS `s`
WHERE (`u`.`Id` = `s`.`UserId`) AND (`s`.`CourseName` = '语文')
结果
3.5.1.2 Linq标准查询运算符
var list = _context.Users.Where(t => t.Account != null)
.Join(_context.Scores.Where(sc => sc.CourseName == "语文"), u => u.Id, sc => sc.UserId, (u, sc) => new
{
u.Account,
u.PassWord,
sc.CourseName,
sc.Grade
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(list));
生成SQL
# 不加查询课程
SELECT `u`.`Account`, `u`.`PassWord`, `s`.`CourseName`, `s`.`Grade`
FROM `user` AS `u`
INNER JOIN `score` AS `s` ON `u`.`Id` = `s`.`UserId`
# 查询课程
SELECT `u`.`Account`, `u`.`PassWord`, `t`.`CourseName`, `t`.`Grade`
FROM `user` AS `u`
INNER JOIN (
SELECT `s`.`CourseName`, `s`.`Grade`, `s`.`UserId`
FROM `score` AS `s`
WHERE `s`.`CourseName` = '语文'
) AS `t` ON `u`.`Id` = `t`.`UserId`
结果
3.5.2 外连接
外连接join后必须有into,然后可以加上XX.DefaultIfEmpty(),表示对于引用类型将返回null,而对于值类型则返回0。对于结构体类型,则会根据其成员类型将它们相应地初始化为null(引用类型)或0(值类型),
如果仅需要统计右表的个数或者其它属性,可以省略XX.DefaultIfEmpty, 但如果需要点出来右表的字段,则不能省。
3.5.2.1 linq实现
查询所有用户对应的班级,因为用户和成绩一对多,所以会出现多条数据
var list = (from u in _context.Users
join sc in _context.Scores on u.Id equals sc.UserId
into ulist
from sco in ulist.DefaultIfEmpty()
where u.Account != null //这个条件只是展示如何添加条件
select new
{
UserId = u.Id,
Account = u.Account,
sco.CourseName
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(list));
生成SQL
SELECT `u`.`Id` AS `UserId`, `u`.`Account`, `s`.`CourseName`
FROM `user` AS `u`
LEFT JOIN `score` AS `s` ON `u`.`Id` = `s`.`UserId`
结果
如果要查询成绩,应该这么写,上面那个写法会直接报错, Nullable object must have a value
3.5.3 GroupJoin
GroupJoin操作符常应用于返回“主键对象-外键对象集合”形式的查询,例如“用户信息-此用户下所有科目成绩”
var list = _context.Users.Where(t => t.Account != null)
.GroupJoin(_context.Scores, u => u.Id, sc => sc.UserId, (u, sc) => new
{
u.Account,
u.PassWord,
Scores = sc
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(list));
该代码会提示错误,原因如:https://docs.microsoft.com/zh-cn/ef/core/querying/client-eval
3.5.4 GrouBy
分组操作 根据用户分组,求科目数
var list = (from sc in _context.Scores
group sc by sc.UserId
into g
select new
{
g.Key,
Count = g.Count()
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(list));
var list2 = _context.Scores.GroupBy(sc => sc.UserId).Select(t => new
{
t.Key,
Count = t.Count()
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(list2));
生成SQL
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `s`.`UserId` AS `Key`, COUNT(*) AS `Count`
FROM `score` AS `s`
GROUP BY `s`.`UserId`
[{"Key":"1395392302788120576","Count":2},{"Key":"1395392303090110464","Count":2}]
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `s`.`UserId` AS `Key`, COUNT(*) AS `Count`
FROM `score` AS `s`
GROUP BY `s`.`UserId`
[{"Key":"1395392302788120576","Count":2},{"Key":"1395392303090110464","Count":2}]
4. 添加
4.1 基础添加
_context.Movie.Add(movie);
// or
await _context.Movie.AddRangeAsync(movies)
await _context.SaveChangesAsync();
4.2 已经设置自增键的插入
先关闭自增然后插入数据后再开启自增
db.Database.OpenConnection();
db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [T_RoleInfor] ON");
var r2 = new T_RoleInfor()
{
id = 123,
roleName = "管理员",
roleDescription = "我是管理员"
};
db.Add(r2);
int count2 = db.SaveChanges();
db.Database.ExecuteSqlCommand("SET ID ENTITY_INSERT [T_RoleInfor] OFF");
4.3 通过SQL添加
var strSql2 = "INSERT INTO `userinfo`(`Id`, `Account`, `PassWord`) VALUES (@id, @account, @password);";
var parameter2 = new MySqlParameter[] {
new MySqlParameter("@id","22"),
new MySqlParameter("@account","2222"),
new MySqlParameter("@password","22222")
};
var flg = db.Database.ExecuteSqlRaw(strSql2, parameter2);
5. 修改
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
movie.Name="李思";
await _context.SaveChangesAsync();
6. 删除
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
_context.Movie.Remove(movie);
await _context.SaveChangesAsync();
7. 参考文档
官方例子:https://docs.microsoft.com/zh-cn/ef/core/dbcontext-configuration/
微信公众号
EFCore之增删改查的更多相关文章
- abp(net core)+easyui+efcore仓储系统——展现层实现增删改查之控制器(六)
abp(net core)+easyui+efcore仓储系统目录 abp(net core)+easyui+efcore仓储系统——ABP总体介绍(一) abp(net core)+easyui+e ...
- abp(net core)+easyui+efcore实现仓储管理系统——展现层实现增删改查之列表视图(七)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——ABP WebAPI与EasyUI结合增删改查之一(二十七)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- ASP.NET CORE系列【三】使用Entity Framework Core进行增删改查
身份验证 以前我们熟悉的web.config中配置的form验证,现在没有了.我们来看看在Core里面如何配置: 首先需要NuGet安装一个包:Microsoft.AspNetCore.Authent ...
- 国产化之路-统信UOS + Nginx + Asp.Net MVC + EF Core 3.1 + 达梦DM8实现简单增删改查操作
专题目录 国产化之路-统信UOS操作系统安装 国产化之路-国产操作系统安装.net core 3.1 sdk 国产化之路-安装WEB服务器 国产化之路-安装达梦DM8数据库 国产化之路-统信UOS + ...
- Dapper逆天入门~强类型,动态类型,多映射,多返回值,增删改查+存储过程+事物案例演示
Dapper的牛逼就不扯蛋了,答应群友做个入门Demo的,现有园友需要,那么公开分享一下: 完整Demo:http://pan.baidu.com/s/1i3TcEzj 注 意 事 项:http:// ...
- ASP.NET从零开始学习EF的增删改查
ASP.NET从零开始学习EF的增删改查 最近辞职了,但是离真正的离职还有一段时间,趁着这段空档期,总想着写些东西,想来想去,也不是很明确到底想写个啥,但是闲着也是够 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(9)-MVC与EasyUI结合增删改查
系列目录 文章于2016-12-17日重写 在第八讲中,我们已经做到了怎么样分页.这一讲主要讲增删改查.第六讲的代码已经给出,里面包含了增删改,大家可以下载下来看下. 这讲主要是,制作漂亮的工具栏,虽 ...
- 通过Java代码实现对数据库的数据进行操作:增删改查
在写代码之前,依然是引用mysql数据库的jar包文件:右键项目-构建路径-设置构建路径-库-添加外部JAR 在数据库中我们已经建立好一个表xs :分别有xuehao xingming xue ...
随机推荐
- Linux基础之Shell与变量
一.提出问题 在平时的工作中,我们经常会碰到设置环境的问题,例如将应用的执行路径添加到PATH中,方便程序的执行:在Linux中更多的时候是跟shell打交道,很多通过shell启动的应用或者服务都需 ...
- IT培训有哪些坑(二)?
今天继续给大家分享一下IT培训都有哪些坑?有哪些不靠谱? 做招转的不靠谱.什么是招转?就是招聘转招生,名义上说的是招聘,但实际上做的就是招生.有很多大学刚毕业的计算机相关专业的同学,他们大学毕业之后, ...
- day-04-列表
列表的初识 容器型数据类型,可以承载大量的数据,有序的数据 why:int bool str:'123 True 太白' str: 存储少量的数据. str:切片还是对其进行任何操作,获取的内容全都是 ...
- java面试-Java内存模型(JMM)
p.p1 { margin: 0; font: 15px Helvetica } 一.并发编程两个关键问题 线程之间如何通信.同步.java并发采用的是共享内存模型 二.JMM内存模型的抽象结构 描述 ...
- OO_Unit3_Summary
JML这一单元是真的有含金量,很有难度.而且这难点和前两单元完全不同,前两单元是容易架构混乱导致细节出问题,JML单元是读不懂JML规格的话架构都构不出来,以及即使能够读懂JML规格了,让自己写规格的 ...
- 【笔记】《Redis设计与实现》chapter8 对象
8.1 对象的类型与编码 Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性.encoding属性和ptr属性 typedef struc ...
- Spring 学习笔记(二):SpringIoC
1 IoC与DI IoC是Inversion of Control的简称,也就是控制反转.通常来说,创建对象需要调用者手动创建,也就是new XXX()的方式.当Spring框架出现后,对象的实例不再 ...
- OkHttp:NoClassDefFoundError
1 问题描述 使用OkHttp时报错: Caused by: java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics at okh ...
- ElasticSearch-02-elasticsearch.yaml
# ======================== Elasticsearch Configuration ========================= # # NOTE: Elasticse ...
- MVVM框架理解
MVC框架 将整个前端页面分成View,Controller,Modal,视图上发生变化,通过Controller(控件)将响应传入到Model(数据源),由数据源改变View上面的数据. 整个过程看 ...