ORM_EF

ORM_EF

O/RM对象关系映射

Ado.net 操作数据库

进程交互 靠的是网络协议

image-20211026212957887

a.写起来比较麻烦 b.开发者得知道sql

c.不同得表写不同得sql d.开发效率低一些

ORM

ORM是一个封装,是一个代理

ORM会把整个数据库给你托管程序里面进行封装(类)

通过操作这些类,就可以完成对数据库的操作可以不用关心sql,不用写sql

底层还是基于Ado.net + sql语句实现的

image-20211026093721264

常用的ORM框架

image-20211026093909190

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实体数据模型

image-20211026100324389

选择DBFirst

image-20211027142453469

新建连接

image-20211026102652242

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

image-20211026220944327 image-20211026102758537 image-20211026102828205

DBFirst生成的文件和配置

image-20211026103022041

image-20211026103117915

image-20211026101744385

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

image-20211027143641782

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

image-20211027143754779

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
image-20211027102137464
join/DefaultIfEmpty
image-20211027102156906
执行SQL语句

///查询

dbContext.Database.SqlQuery<"表名">(sql,Sparamter);

///更新

dbContext.Database.SqlParameter<"表名">(sql,paramter);
image-20211027174955839

image-20211027102634924

注意:

无论怎么玩花样,最终都是生成一整条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就修改数据库
image-20211027105037765

image-20211027105641493

DbSet.Attach(Object)

官方解释:将实体以“未更改”的状态放置到上下文中,就好像从数据库读取了该实体一样。image-20211027110928142

image-20211027111047209

image-20211027111409607

image-20211027111820716

6、EF上下文生命周期/事务(2)

Context解析:

整个进程就一个context实例

image-20211028101413478

image-20211028102307595

多个context实例join不行

image-20211028101933884

7、EF延迟查询,导航属性(2)

  • 新建一个EFAdvancedTest.cs类

    image-20211028104828149

只有在Context作用域内有效

image-20211028104958626

不启用延迟查询,直接查询到内存中

image-20211028105315589

image-20211028110234992

linq to object时循环的时候才进行查询

image-20211028105853088

表达目录树

image-20211028110200269

///


/// 分页查询
///

///
///
///
///
///
///
///
///
PageResult QueryPage<T, S>(Expression<Func<T, bool>> funcWhere, int pageSize, int pageIndex, Expression<Func<T, S>> funcOrderby, bool isAsc = true) where T : class;

参考资料

学习视频:https://www.bilibili.com/video/BV19J411v77u

posted @ 2021-10-29 16:37  CCmonitor  阅读(80)  评论(0)    收藏  举报