ORM_EF
ORM_EF
O/RM对象关系映射
Ado.net 操作数据库
进程交互 靠的是网络协议
a.写起来比较麻烦 b.开发者得知道sql
c.不同得表写不同得sql d.开发效率低一些
ORM
ORM是一个封装,是一个代理
ORM会把整个数据库给你托管程序里面进行封装(类)
通过操作这些类,就可以完成对数据库的操作可以不用关心sql,不用写sql
底层还是基于Ado.net + sql语句实现的
常用的ORM框架
O/RM究竟是怎么实现的呢?怎么选择呢
-
Sql---反射生成sql---自动执行---反射绑定结果
-
大量的反射 性能影响--缓存一下--占内存--启动慢执行不影响
-
开发快捷,降低学习SQL的成本
-
缺点:SQL固定生成,但是僵化,对索引利用不够好,分页算法就不够好,在复杂的情况下难以应对,ORM工具一般也可以支持写SQL
-
ORM一般还能适应不同数据库的迁移
-
个人想法:O/RM只是一个工具,去完成它擅长的事情,也有的事是它做不好的,也只能去结合别的工具来协作完成的,O/RM实际上是一种面向对象的做法,是一种思想上的进步,但是这个进步是有代价的,影响一定的性能。
Entity-Framework6(EF6)
支持各种版本的数据库,数据库存储过程,函数
都是由微软提供,和VS完美结合!在.net core时代对于EF Core
1、EF编程三种方式
DBFirst:数据库先行,先建立好数据库,然后通过数据库映射实体,edmx文件,然后就可以直接使用Context查询数据。
CodeFirst:代码先行
ModelFirst:模型先行
创建示例
- 项目右键属性->添加->新建项->数据->ADO.net实体数据模型
选择DBFirst
新建连接

注意此处选择[是]:不然会报错



DBFirst生成的文件和配置

2、EF的三种映射方式
2.1 特性映射
//实体中添加特性
[Table("数据库表的名称")]
[Column("数据库字段名称")]
//myDBContext中添加DbSet
public virtual DbSet<C__EFMigrationsHistory> C__EFMigrationsHistory { get; set; }
2.2 直接写在映射关系方法里面
modelBuilder.Entity<类名称>().ToTable(表名称).Property(c=>c.属性名称).HasColumnName(数据库字段名称)
//在DBFirstContext类的OnModelCreating方法
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<account>().ToTable("account").Property(c=>c.Name).HashColumnName("text");
}
2.3 参考NHibernate建立一个EF自带的映射文件
///新建mapping类
public class StorageEntityMapping : EntityTypeConfiguration<StorageEntity>
{
public StorageEntityMapping()
{
this.ToTable("StorageEntity");
}
}
///Context中添加对应mapping类
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new StorageEntityMapping());
}
3、监视EF执行
1.SQL server Profiler

通过过滤hostName可过滤非本机操作数据库的SQL语句

2.通过Context输出日志
- db.Database.Log += c => Console.WriteLine($"sql:{c}");
///在DBcontext构造函数中输出调用日志
public JDDbContext()
: base("name=JDDbContext")
{
this.Database.Log += c => Console.WriteLine($"sql:{c}");
}
///输出EF在控制台输出日志
using (var dbContext = new DBFirstContext())
{
dbContext.Database.Log += r => Console.WriteLine(r);
var student = dbContext.Students.Find(1);
Console.WriteLine($"student.id:{student.Id} student.Name:{student.Name}student.ClassName:{student.ClassName}");
}
///将日志输出到文件中(https://www.cnblogs.com/CreateMyself/p/4753476.html)
using (var ctx = new EntityDbContext())
{
var sw = new StreamWriter(@"d:\Data.log") { AutoFlush = true };
ctx.Database.Log = s => {
sw.Write(s);
};
}
4、复杂查询&执行SQL(2)
In查询 :通过Contains关键字
///取出ID为1,2,3,4的数据
using (var context = new MyDBContext())
{
var list = context.Students.Where(r => new int[] { 1, 2, 3, 4 }.Contains(r.Id));
foreach (var item in list)
{
Console.WriteLine(item.Name);
}
}
排序/投影/分页:OrderBy/Select/Skip(3).Take(5)
var list = context.Students.OrderBy(u => u.Id).Select(u => new
{
Name = u.Name,
Email = u.Email
}).Skip(3).Take(5);
foreach (var item in list)
{
Console.WriteLine(item.Email);
}
StartsWith/EndsWith
join/DefaultIfEmpty
执行SQL语句///查询
dbContext.Database.SqlQuery<"表名">(sql,Sparamter);
///更新
dbContext.Database.SqlParameter<"表名">(sql,paramter);
注意:
无论怎么玩花样,最终都是生成一整条SQL语句,一次性到数据库查询....在底层最终还是Ado.net
SaveChanges是以context为维度,如果监听到任何数据的变化,然后会一次性的保存到数据库去,而且会开启事务。
5、EF状态跟踪(2)
EF实体状态(EntityState)
Detached,Unchanged,Added,Deleted,Modified
context.Entry
(user).State 获取实体上下文的状态 Detached:和Context完全没有任何关系,不受Context跟踪
Unchanged:受Context跟踪,并存在于数据库中,属性值和数据库中的值相等。
Added:受Context跟踪,数据库中还不存在,SaveChanges就添加到数据库
Deleted:受Context跟踪,存在于数据库中,但已经存在标记为在下一次调用SaveChanges时从数据库中删除
Modified:受Context跟踪,并存在于数据库中,并且它的部分或全部属性值已被修改。SaveChanges就修改数据库
DbSet.Attach(Object)
官方解释:将实体以“未更改”的状态放置到上下文中,就好像从数据库读取了该实体一样。
6、EF上下文生命周期/事务(2)
Context解析:
整个进程就一个context实例
多个context实例join不行
7、EF延迟查询,导航属性(2)
-
新建一个EFAdvancedTest.cs类
只有在Context作用域内有效
不启用延迟查询,直接查询到内存中
linq to object时循环的时候才进行查询
表达目录树
///
/// 分页查询
///
///
///
///
///
///
///
///
///
PageResult