第二十三节:EFCore6.0反向测试、增删改查、主键问题、EFCore操作DB原理 、翻译输出SQL的3种方式
一. EFCore6.0反向测试
1. 需要的程序集
必须的程序集: Microsoft.EntityFrameworkCore.Tools
EF自身的程序集:Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
PS:【Microsoft.EntityFrameworkCore.SqlServer】:里面包含【Microsoft.EntityFrameworkCore.Relational】,而它里面又包含:【Microsoft.EntityFrameworkCore】
2. 常用指令
(1).全表的首次映射:
【Scaffold-DbContext "Server=localhost;Database=EFCore6xDB;User ID=sa;Password=123456;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Context EFCore6xDBContext -UseDatabaseNames -DataAnnotations -NoPluralize】
(2).全表的后续修改:
【Scaffold-DbContext "Server=localhost;Database=EFCore6xDB;User ID=sa;Password=123456;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Context EFCore6xDBContext -UseDatabaseNames -DataAnnotations -Force -NoPluralize】
更多指令参考:https://www.cnblogs.com/yaopengfei/p/11633385.html
二. 增删改查
直接上代码,没啥可说的
代码分享:
#region 1.1 新增 //{ // using var db = new EFCore6xDBContext(); // UserInfo user = new UserInfo() // { // id = Guid.NewGuid().ToString("N"), // userName = "ypf1", // userPwd = "123456", // userAge = 19, // userGender = "男", // addTime = DateTime.Now, // delflag = 0 // }; // await db.Set<UserInfo>().AddAsync(user); // await db.SaveChangesAsync(); // Console.WriteLine("新增成功"); //} #endregion #region 1.2 修改 //{ // using var db = new EFCore6xDBContext(); // var userList = db.Set<UserInfo>().ToList(); // foreach (var item in userList) // { // item.addTime = DateTime.Now; // item.delflag++; // } // await db.SaveChangesAsync(); // Console.WriteLine("修改成功"); //} #endregion #region 1.3 查询 //{ // using var db = new EFCore6xDBContext(); // var userList = db.Set<UserInfo>().ToList(); // foreach (var item in userList) // { // Console.WriteLine($"{item.userName}, {item.userPwd}, {item.userAge},{ item.userGender}, {item.addTime}, {item.delflag}"); // } //} #endregion #region 1.4 删除 //{ // using var db = new EFCore6xDBContext(); // var user = db.Set<UserInfo>().Where(u => u.userName.StartsWith("ypf")).FirstOrDefault(); // db.Remove(user); // await db.SaveChangesAsync(); // Console.WriteLine("删除成功"); //} #endregion
三. 主键问题
1. 常用的主键类型
(1).自增: 数据库中对应int
(2).Guid: C#代码 Guid.NewGuid(); 数据库中对应uniqueidentifier类型
(3).32位字符串:C#代码Guid.NewGuid().ToString("N"); 数据库中对应varchar(32)
(4).Hi/Lo算法
2. 自增主键剖析
优点:简单;
缺点:数据库迁移以及分布式系统中比较麻烦;并发性能差。long、int等类型主键,默认是自增。因为是数据库生成的值,所以SaveChanges后会自动把主键的值更新到Id属性。
自增字段的代码中不能为Id赋值,必须保持默认值0,否则运行的时候就会报错,要想获取id值,必须借助事务,在savechange之后才能获取,代码麻烦
3. Guid剖析
说明:Guid算法(或UUID算法)生成一个全局唯一的Id。适合于分布式系统,在进行多数据库数据合并的时候很简单。
优点:简单,高并发,全局唯一;
缺点:① 磁盘空间占用大
② Guid值不连续。使用Guid类型做主键的时候,不能把主键设置为聚集索引。因为聚集索引是按照顺序保存主键的,因此用Guid做主键性能差。比如MySQL的InnoDB引擎中主键是强制使用聚集索引的。有的数据库支持部分的连续Guid,比如SQLServer中的NewSequentialId(),但也不能解决问题。
注意:在SQLServer等中,不要把Guid主键设置为聚集索引;在MySQL中,插入频繁的表不要用Guid做主键。
实操:Guid既可以让EF Core自动赋值,也可以手动赋值,推荐手动赋值
#region 1.Guid可以使用EFCore默认赋值(EFCore6.0不支持了,前后生成的都是00-00-00-00)
{
Console.WriteLine("-----------1.Guid可以使用EFCore默认赋值---------------");
using var db = new EFCore6xDBContext();
RoleInfo role = new RoleInfo()
{
roleName = "admin",
roleMsg = "hhh",
addTime = DateTime.Now,
delflag = 0,
};
Console.WriteLine($"保存前的id值为:{role.id}");
db.Set<RoleInfo>().Add(role);
db.SaveChanges();
Console.WriteLine($"保存后的id值为:{role.id}");
}
#endregion
#region 2.Guid可以手动赋值
{
Console.WriteLine("-----------2.Guid可以手动赋值---------------");
using var db = new EFCore6xDBContext();
RoleInfo role = new RoleInfo()
{
id = Guid.NewGuid(),
roleName = "admin",
roleMsg = "hhh",
addTime = DateTime.Now,
delflag = 0,
};
Console.WriteLine($"保存前的id值为:{role.id}");
db.Set<RoleInfo>().Add(role);
db.SaveChanges();
Console.WriteLine($"保存后的id值为:{role.id}");
}
#endregion
4. 32位字符串
相当于把上述的直接使用Guid做主键的模式进行转换了一下,避免上述聚集索引的问题,当数据量小的时候,使用没问题,数据量大,不如自增查询、插入快。
截图:https://www.cnblogs.com/yaopengfei/p/14082555.html 中的灵魂拷问
5. Hi/Lo算法
EF Core支持Hi/Lo算法来优化自增列。主键值由两部分组成:高位(Hi)和低位(Lo),高位由数据库生成,两个高位之间间隔若干个值,由程序在本地生成低位,低位的值在本地自增生成。
不同进程或者集群中不同服务器获取的Hi值不会重复,而本地进程计算的Lo则可以保证可以在本地高效率的生成主键值。但是HiLo算法不是EF Core的标准。
6. 混合自增和Guid(非复合主键)
用自增列做物理的主键,而用Guid列做逻辑上的主键。把自增列设置为表的主键,而在业务上查询数据时候把Guid当主键用。
在和其他表关联以及和外部系统通讯的时候(比如前端显示数据的标识的时候)都是使用Guid列。不仅保证了性能,而且利用了Guid的优点,而且减轻了主键自增性导致主键值可被预测带来的安全性问题。
四. EFCore操作DB原理
EFCore是将C#代码翻译成SQL语句,然后交给Ado.Net core 进行操作数据库的
五. 翻译输出SQL的3种方式
1. 标准日志
(1). 依赖程序集
【Microsoft.Extensions.Logging.Debug】 指的是调试模式下,在vs的输出窗口的输出(适用于各种类型项目的调试, 多用于web项目)
【Microsoft.Extensions.Logging.Console】 指的是控制台程序中输出。
(2). 需要添加的代码
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;Database=EFCore6xDB;User ID=sa;Password=123456;");
//方式1:使用标准日志
optionsBuilder.UseLoggerFactory(LoggerFactory.Create(build =>
{
build.AddConsole(); // 用于控制台程序的输出
build.AddDebug(); // 用于VS调试,输出窗口的输出
}));
}
2. 简单日志
不需要单独引入程序集了,用系统默认的日志即可
默认输出的内容很多,所以要加一层判断,仅输出sql语句即可
添加代码:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;Database=EFCore6xDB;User ID=sa;Password=123456;");
//方式2:使用系统日志
optionsBuilder.LogTo(msg =>
{
//if (!msg.Contains("CommandExecuting"))
//{
// return;
//}
//Console.WriteLine(msg);
if (msg.Contains("CommandExecuting")||msg.Contains("Sql"))
{
Console.WriteLine(msg);
}
});
}
3. 方法直接输出
IQueryable有扩展方法ToQueryString()可以获得SQL。不需要真的执行查询才获取SQL语句;只能获取查询操作的。
{
using var db = new EFCore6xDBContext();
var sql = db.Set<UserInfo>().ToQueryString();
Console.WriteLine(sql);
}
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。