一.DBFirst(SqlServer)
1.引入程序包
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design
命令执行:
Scaffold-DbContext “Data Source=LAPTOP-JU1DEJP1;Initial Catalog=DBSet;User ID=sa;Password=sa123” Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force
2.DFFirst命令执行:
Scaffold-DbContext “Data Source=LAPTOP-JU1DEJP1;Initial Catalog=DBSet;User ID=sa;Password=sa123” Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force
3.命令说明
-OutputDir 实体文件所存放的文件目录
-ContextDir DbContext文件存放的目录
-Context DbContext文件名
-Schemas 需要生成实体数据的数据表所在的模式
-Tables * 需要生成实体数据的数据表的集合
-DataAnnotations
-UseDatabaseNames 直接使用数据库中的表名和列名(某些版本不支持)
-Force 强制执行,重写已经存在的实体文件
二. Codefirst( SqlServer)
- nuget 引入: Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
1.手动建立CustomDbContext,继承DbContext
2.声明链接字符串
3.添加两个方法:
OnConfiguring(DbContextOptionsBuilder optionsBuilder)
OnModelCreating(ModelBuilder modelBuilder)
2. 生成数据库
using (CustomDbContext context = new CustomDbContext())
{
context.Database.EnsureDeleted();//判断是否有数据库,如果有就删除
context.Database.EnsureCreated();//新建立一个数据库
}三.迁移
//在开发过程中,可以存在很多需求变更;变化后,可能会伴随表结构的变化<br /> //可以为每一次变化,生成一个类似于版本的文件---迁移文件<br /> //1.Nuge引入程序包;Microsoft.EntityFrameworkCore.Design<br /> // Microsoft.EntityFrameworkCore.Relational<br /> // Microsoft.EntityFrameworkCore.Tools <br /> //2.工具--程序包管理器控制台--执行命令:Add-migration Init001---生成一个Migrations文件夹<br /> //3.执行命令:update database "迁移文件名称" --映射到数据库中去
四.性能优化篇
1.多活动结果集连接复用
多活动结果集 (MARS) 是一项允许对单个连接执行多个批处理的功能。 在以前的版本中,在单个连接上一次只能执行一个批处理。 使用 MARS 执行多个批处理并不意味着同时执行操作。 在连接字符串中添加:MultipleActiveResultSets=True 即可启用 MARS 特性。
2.批处理语句
EFCore中有一个重大改进,就是批处理,比如向数据库中增加n条数据(n>3),会组合成一次请求访问数据库(而在以前的EF中,不是批处理,增加几条,则会访问几次)。
注:操作数据条数 <=3 的时候,不会批处理,还是分多次请求,只有>3,才会批处理。
PS:可以手动设置批处理的条数MaxBatchSize,默认值很大。optionsBuilder.UseSqlServer(“Server=localhost;Database=EFDB01;User ID=sa;Password=123456;”, b => b.MaxBatchSize(10));
3.非跟踪查询
_dataContext.Region.AsNoTracking().ToListAsync();
关于同步状态:
当从数据库进行查询数据时,上下文便捕获了每个实体属性的快照(数据库值,原始值,当前值),当调用SaveChanges 时,在内部会自动调用 DetectChanges 方法,此方法将扫描上下文中所有实体,并比较当前属性值和存储在快照中的原始属性值,如果被找到的属性值发生了改变,此时EF将会与数据库进行交互,进行数据更新。会导致自动调用 DetectChanges 方法: Find、Local、Remove、Add、Update、Attach、SaveChanges 和Entry 等。 但是,自动同步状态会频繁调用,可手动关闭以上方法的自动同步,当数据都修改好后,一次性手动同步。
关闭:context.ChangeTracker.AutoDetectChangesEnabled = false;
开启:context.ChangeTracker.DetectChanges();
4.非跟踪增加
_dataContext.Entry(region).State = EntityState.Added;
int res = await _dataContext.SaveChangesAsync();
5.非跟踪修改
_dataContext.Entry(region).State = EntityState.Modified;
int res = await _dataContext.SaveChangesAsync();
6.非跟踪删除
_dataContext.Entry(region).State = EntityState.Deleted;
int res = await _dataContext.SaveChangesAsync();
7.正确使用Find/FirstOrDefault
正确使用Find(id=10)来代替FirstOrDefault(t=>t.id=10)
Find会优先查询缓存,当前面已经查询过这条数据的时候使用,而FirstOrDefault每次都会查询数据库;当id=10的数据被修改之后,find查出的数据是新数据。
8.区分IQueryable和IEnumerable
8.正确区分IQueryable和IEnumerable
8.1 IEnumerable(linq to object)用于操作内存对象。是个迭代器的实现。
8.1.1 Where(t=>t.id>10)中的“t=>t.id>10”是个委托
8.1.2 封装的函数的时候将返回值设为IEnumerable
8.2 IQueryable(linq to sql)用于操作数据库,且继承了IEnumerable。 IQueryable中实现了表达式目录树(Expression),IQueryProvider根据表达式目录树来构建sql语句。
6.2.1 Where(t=>t.id>10)中的“t=>t.id>10”是个表达式目录树
6.2.2 AsEnumerable() 和 AsQueryable()如果后面不继续跟过滤条件等,效果是一样的。 如果后面加了Where / Select / Take() /Skip 等条件,AsEnumerable()先查数据库再过滤,AsQueryable()将条件生成sql,一起在数据库中过滤。
9.导航属性延迟查询(延迟加载)
9.正确使用导航属性 和 延迟查询(延迟加载)
在主表对象中包含一个子表集合对象的属性就是导航属性。跟数据库中的主外键设置无关。
利用延迟加载可以叠加多次查询条件,一次性提交给数据库。
使用建议:在开发中不确定后面是否需要关联表数据,可以使用延迟加载来按需获取数据。
9.1 导航属性要延迟加载必须具备两个条件:
a、导航属性是virtual的;
b、延迟查询必须是开启的。
EF:context.Configuration.LazyLoadingEnabled=true; (默认就是true的)
EF Core: protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
var builder = optionsBuilder.UseSqlServer(_connStr);
optionsBuilder.UseLazyLoadingProxies(); //启用延迟加载
}
9.2 延迟查询关闭之后可以使用显示加载的方式:
加载集合: context.Entry<Company>(company).Collection(t=>t.Users).Load();
加载单个: context.Entry<Company>(company).Reference(t=>t.Users).Load();
9.3 使用ToList()的时候会立马执行数据库sql查询,看到过很多同学先ToList()再Where()过滤。
其它的还有:Count() 、FirstOrDefault()、迭代器IEnumerable/foreach 等都会立刻执行sql。
封装函数的时候可以返回IQueryable<T>,而不是IEnumable<T>,防止立马查询数据库。
9.4 迭代(如foreach)使用延迟查询的时候,迭代完了才关闭链接,应当避免使用这种场景。可以在迭代之前ToList()。
9.6 延迟加载的对象(IQueryable
10.预先加载
10.预先加载
使用Include,查询主表时把子表(导航属性)也一次性查出来。延迟查询关闭后也不影响的。
例如:context.Set
使用建议:当开发中能确定后面一定会用到子表数据的时候可以使用预先加载。
11.使用TransactionScope
11.使用TransactionScope
TransactionScope可以完成多个context多个SaveChange的事务问题,默认一个SaveChange是一个事务
还可以使用context.Database.BeginTransaction做单库事务。
using (DbContext context = new DbContext())
{
using (TransactionScope tran = new TransactionScope())
{
context.SaveChange();
context.SaveChange();
//无异常会执行Complete 提交事务
tran.Complete();
}
}
12.
添加Z.EntityFramework.Plus.EFCore依赖使用一些特殊的语法
这个是免费的,但 Z.EntityFramework.Plus的一些批量数据操作的包是收费的
10.1 EFCore删除必须先查询再删除,优化后可直接删除:context.User.Where(t => t.Id == 100).Delete();
10.2 优化更新语句:context.User.Where(t => t.Id == 4).Update(t =>new User() { NickName = “2224114” ,Phone = “1234”} );
12.索引支持
13.LinqShow
14.Z.EntityFramework.Extensions.EFCore
var userlist = context.SysUser.Take(100000).Delete();
var userlist = context.SysUser.Take(100000).Update(u=>new Sysuser(){});
var userlist = context.SysUser.Take(100000).Update(u=>new Sysuser(){}); ;
//1.扩展类
DbContextExtensions