什么是ORM

起源随着编程的发展,程序里都是面向对象啥的,但是数据库发展呢  网状数据库 -》层次数据库 -》关系数据库(当然还有nosql数据库  我们只是做热数据缓存  后面将会讲到) 。关系型数据库一直流行到当今。

就出现了一个问题,程序里的发展和数据库的发展不匹配,一个面向对象 类什么,一个是行列结构    而且数据库的多样性,这种不协调就做阻抗失衡,所以就出现了ORM(Object Relational Mapping)。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据(.net中这种类叫做POCO类,没有任何业务逻辑,数据库类最好也用CLR中的数据类型  例如  system.int 这种的),将程序中的对象自动持久化(持久化就是保存)到关系数据库中。

EF三种模式

其实这个是随着 EF版本的发展以及ObjectContext到DBContex 的发展而发展的。

1、DBFirst

就是先把数据库设计好,然后用在VS项目中添加新建项,添加一个来自数据库的EF设计器  edmx。(edmx是一个组件,最后会生成一个dll,里面有很多类,类的生成是根据T4模板生成的。T4模板的底层使用的是codedom)

上图中 选择模型内容,以前的版本中只有两个,后来变成四个。以前还有一个ef power tools,现在都用用不到了。本人认为上图中  来自数据库的Code First生成的代码使用的技术是codedom实现的。我是自己写的工具底层codedom,生成实体 以及一些其他数据操作类,业务操作类等。

EDMX其实就是一个xml文件,运行的时候被分为三个子文件,

*.csdl(conceptyal  shema definition)概念模型(conceptual model),就是实体类。

*.ssdl(storage schema definition language) 存储模型(storage model),底层存储操作类。

*.msl(mapping specification language) 概念-存储模型映射,概念模型于存储模型的映射。

程序中EF第一使用会生成这个映射到缓存里。并且执行 protected override void OnModelCreating(DbModelBuilder modelBuilder)。这个方法也只会执行一次,以后再也不执行了。所以关于实体的配置一般都写在这个里面。能不写在构造函数里面的就不写在里面。但是禁用状态跟踪写在这个里没有效果(this.Configuration.AutoDetectChangesEnabled = false;)

所以大家说的第一次加载慢  就是因为这个操作,当然还有其他的原因。

//EF Pre-Generated Mapping Views(预生成映射视图)
using (var dbcontext = new CustomContext())
{
var objectContext = ((IObjectContextAdapter)dbcontext).ObjectContext;
var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
//DataSpace.CSSpace C 表示概念模型 S表示存储模型
mappingCollection.GenerateViews(new List<EdmSchemaError>());
//对程序中定义的所有DbContext逐一进行这个操作
}

这段代码一般放在项目入口问题,只执行一次。ASP.NET中放在Application_Start里面

2、ModelFirst

就是先把模型edmx设计好,然后在通过edmx把模型跟新到数据库里面。

3、CodeFirst

没有了edmx,没有了乱七八糟的东西,都可以靠自己定制。更确切的说叫做code only

总结的来说就是一个映射ORM过程。

ObjectContext-->DBContext

老版本中的是ObjectContext,后来4.0后升级为DBContext

public class DbContext : IDisposable, IObjectContextAdapter

public interface IObjectContextAdapter
{
//
// 摘要:
// 获取对象上下文。
//
// 返回结果:
// 对象上下文。
ObjectContext ObjectContext { get; }
}

可以看出ObjectContext和DBContext中间多了一个适配器。DBContext中有加入了一些操作。

EF增删改

增加

先把实体类填充好,然后在context.DbSet.Add(.);

最后SaveChange(),一次性提交当前上下文中所有变更的实体。

Class c1 = new Class();
c1.ClassAddress = "北京壹号四合院1";
c1.ClassName = "四合院名字1";
c1.ClassNum = ;
Student st1 = new Student();
st1.StudentAddress = "学生地址1";
st1.StudentAge = ;
st1.StudentName = "学生名字1";
st1.StudentSex = ;
c1.Student.Add(st1);
Student st2 = new Student();
st2.StudentAddress = "学生地址2";
st2.StudentAge = ;
st2.StudentName = "学生名字2";
st2.StudentSex = ;
c1.Student.Add(st2);
context.Class.Add(c1);
var f = context.SaveChanges();

删除

删除操作也是先加载一个实体,然后删除实体。最后savechange。

(实体可以查询数据库得到,也可以通过指定实体主键,然后加载上下文中。DbSet.Attach)

var stu = context.Student.FirstOrDefault();
if (stu != null)
{
var remStu = context.Student.Remove(stu);
var remFlag = context.SaveChanges();
Console.WriteLine("删除成功studentID{0},删除数量{1}", stu.StudentID, remFlag);
}

修改

直接修改加载到上下文中的实体,然后修改,最后SaveChange.

var stu = context.Student.FirstOrDefault();
if (stu != null)
{
stu.StudentAddress = "我更新了一下学生信息,更新时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
stu.StudentAge = ;
var remFlag = context.SaveChanges();
Console.WriteLine("更新成功studentID{0},更新数量{1}", stu.StudentID, remFlag);
}

事物

1、SaveChange会把上下文中所有的修改的实体生成SQL提交给数据库。(不会锁表,一个SQL语句提交)

2、一个context中一个SaveChange就是一个事物。

也可以在context中开启事物。(会锁表,即使事物里面 有个多savechange也会开启事物)

using (var tr = context.Database.BeginTransaction().UnderlyingTransaction)
{
try
{
string strCom = "INSERT INTO Book(BookName,BookPage,BookUser)VALUES(@BookName,@BookPage,@BookUser)";
SqlParameter sp1 = new SqlParameter("@BookName", "我是通过实物提交上来的书名 保存失败测试");
SqlParameter sp2 = new SqlParameter("@BookPage", );
SqlParameter sp3 = new SqlParameter("@BookUser", "BookUser实物 保存失败测试");
var fsql = context.Database.ExecuteSqlCommand(strCom, sp1, sp2, sp3);
Class c1 = new Class();
c1.ClassAddress = "北京壹号四合院实物1 保存失败测试";
c1.ClassName = "四合院名字实物1";
c1.ClassNum = ;
Student st1 = new Student();
st1.StudentAddress = "学生地址实物1";
st1.StudentAge = ;
st1.StudentName = "学生名字实物1";
st1.StudentSex = ;
c1.Student.Add(st1);
Student st2 = new Student();
st2.StudentAddress = "学生地址实物2";
st2.StudentAge = ;
st2.StudentName = "学生名字实物2";
st2.StudentSex = ;
c1.Student.Add(st2);
var eflag = context.Class.Add(c1);
context.SaveChanges(); //设置失败保存
//context.Database.ExecuteSqlCommand("失败保存");
tr.Commit();
Console.WriteLine("保存成功{0},{1}", fsql, eflag);
}
catch (Exception e)
{
tr.Rollback();
Console.WriteLine("保存失败:{0}", e.Message);
} }

3、多个context一起提交 事物(支持分布式事物,但是需要配置windows服务)

private TransactionScope trans = null;
public UnitOfWork()
{
trans = new TransactionScope();
} public void Commit()
{
if (trans != null)
{
trans.Complete();//必须要调用scope.Complete()才能将数据更新到数据库
}
} public void Dispose()
{
if (trans != null)
{
trans.Dispose();
}
}
EF查询

ObjectContext对象(可以不用学习这一部分)

1、Entity SQL是一种类似SQL的查询语言(ESQL),NH中的是HSQL。Entity SQL查询对象是EDMX而不是数据库中的表。

select value c from 上下文名字.实体名 as c

value关键字希望你可以返回一个强制类型集合,如果没有这个关键字,那么返回的就是一个数据库中的二维表 table。

Entity SQL 调用 Object Services

2、LINQ TO Entities

通过linq查询ADO.NET实体数据模型。底层使用对象服务(object services)。他可以将查询结果转过为强类型的CLR对象。

3、EntityClient

不管使用EntitySQL还是Linq to entities最终都要依赖Entity Client完成工作。

我们可以在程序中使用entity client查询来存取数据,但是只能通过编写entity sql(entity client 类似ADO.NEt),性能可以改善,到那时不得不手动跟踪对象状态。所以这种方式建立使用批量读取且读完后不做数据修改的操作,

上面三种都适用于ObjectContex

4、直接执行SLQ

在ObjectContext中可以直接

objectContext.ExecuteStoreCommand
objectContext.ExecuteStoreQuery

在DBContext中

context.Database.ExecuteSqlCommand
context.Database.SqlQuery

5、使用linq和扩展方法+lambda

实际上linq to entities查询最终转化为扩张方法+lambda。然后在转化成数据库的SQL。个人觉得增删改用实体对象操作,查询直接执行SQL  存储过程操作,查询的也可以做读写分离。

DBContext对象

1、查询表达式和扩展方法+lambda(linq)

以前写的文章:C#-LINQ

本文对应的代码中也会有常用的查询例子(见最下方下载)。

这里没有涉及到表达式树的知识,讲在后面文章专门写到。东西实在太多了,都是基础。

特殊查询过滤函数:

find

include

2、直接执行SQL

context.Database.SqlQuery<T>执行一个查询语句,返回的默认是一个列表,可以设置参数化查询。T 表示返回的值会自动转化为该类型的列表形式。

context.Database.ExecuteSqlCommand执行一个数据库语句,返回值是一个受影响的行数,可以设置为参数化查询。一般为增删改操作,但是也可以为select查询等其他操作。

string strCom = "INSERT INTO Book(BookName,BookPage,BookUser)VALUES(@BookName,@BookPage,@BookUser)";
SqlParameter sp1 = new SqlParameter("@BookName", "我是通过实物提交上来的书名 保存失败测试");
SqlParameter sp2 = new SqlParameter("@BookPage", );
SqlParameter sp3 = new SqlParameter("@BookUser", "BookUser实物 保存失败测试");
var fsql = context.Database.ExecuteSqlCommand(strCom, sp1, sp2, sp3);

3、存储过程、视图

  • 视图也可以建一个实体类,创建方式和表的实体类创建一样,这里就不多说了(当然也可以直接写SQL查询SqlQuery<T>,类型转换)。

    具体的看本文代码。

  • 存储过程其实就是执行SQL

    context.Database.SqlQuery<ClaStu>("EXEC pClaStu");

    ("exec pro_XXX @i,@j,@he output", parameters)

4、关于一些缓存问题

  • 当前上下文对象缓存

DbSet<TEntity>.Local  定义:它表示此集中的所有“已添加”、“未更改”和“已修改”实体的本地视图。在上下文中添加或删除实体时,该本地视图将保持同步。同样,在本地视图中添加或删除实体也会自动在上下文中添加或删除实体。

意思就是说,我在当前上下文中做了数据操作,都会保存到本地。

其中缓存可以监控,是一个观察者模式。

context.Student.Local.CollectionChanged += (sender, e) => {
Console.WriteLine("-------start------");
if (e.NewItems != null)
{
foreach (Student item in e.NewItems)
{
Console.WriteLine("ADD:"+item.StudentName);
}
}
if (e.OldItems != null)
{
foreach (Student item in e.OldItems)
{
Console.WriteLine("Remove:" + item.StudentName);
}
}
Console.WriteLine("-------end------");
};
  • 查询缓存(并没有执行SQL)

Find

5、关于延迟加载问题

  • IQueryable
  • 导航属性
  • Virtual关键字

上下文中加入的实体类前面加virtual关键字。

导航属性

导航属性包含virtual关键字,当时访问到这个导航属性的时候,才会加载这个导航属性。

public Class()
{
Student = new HashSet<Student>();
}
public virtual ICollection<Student> Student { get; set; }
public long ClassID { get; set; }
[ForeignKey("ClassID")]
public virtual Class Class { get; set; }

如果导航属性不加这个关键字,则导航属性为空集合 ,

对象状态跟踪

状态跟踪原理

EF中对象跟踪使用的是一个DbChangeTracker对象来跟踪用户操作。当EF从查询结果总取到实体的时候,他会同步创建一个DbEntityEntity对象来记录实体的变化。一个实体实例就有一个对应的DbEntityEntity。只要对象活着他的DbEntityEntity也活着。DbContext. ChangeTracker对象在适当的时间自动检查对象属性值的更改或DbSet对象集合中对象个数的变化,负责同步更新对应的DbEntityEntry对象。我们可以使用DbContext.Entry(entity)方法获取entity所对应的状态对象,从而了解对象的相关信息。

适当的时间:

DbSet.Add
DbSet.Find
DbSet.Remove
DbSet.Local
DbContext.SaveChanges
Running any LINQ query against a DbSet
DbSet.Attach
DbContext.GetValidationErrors
DbContext.Entry
DbChangeTracker.Entries

如果禁用了状态跟踪Configuration.AutoDetectChangesEnabled = false;上面的动作都不会更新状态。如下图。

获取状态代码,主要是要得到实体对象对应的DbEntityEntity对象。context.Entry(book)  具体的看本文代码,下方下载

BookName修改了,但是状态还是unchanged   ,bookname的修改状态也是false。(如果想改变状态就要手动调用ChangeTracker.DetectChanges()方法)

但是删除还是会修改状态的。

但是最后提交的时候DbContext.SaveChanges(),此方法在内部会调用 ChangeTracker.DetectChanges()方法,根据它所管理的所有的DbEntityEntry对象的状态生成相应的Insert,Delete,Update命令。并负责将这些命令发送给数据库。(所以我们一般禁用状态跟踪,可以提高一点性能)

禁用状态跟踪

数据是只读的,那么,可以禁用状态跟踪以获取较优的性能

context.DbSet.AsNoTracking();   查询语句

this.Configuration.AutoDetectChangesEnabled = false;    这一句只能写在上下问的构造函数里面。

常用实体特性

1、Key主键特性

EF默认ID id  表明ID是主键,需要是自增长整形的。

[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

2、外键

[ForeignKey("ClassID")]
public virtual Class Class { get; set; }

3、表

[Table("Student")]

[Table("Student",Schema ="dbo")]

4、长度

[MinLength(10),MaxLength(30)]
public string Name { get; set; }
5、非空

[Required(ErrorMessage="请输入描述")]
6、列
[Column(Order =1,TypeName = "Timest")]
7、忽略映射,可以应用于 自定义属性 非数据库字段。
[NotMapped]
8、时间戳长于[ConcurrencyCheck]一起用,判断版本号
[Timestamp,DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public byte[] Timest { get; set; }

9、[ConcurrencyCheck]

生成的SQL中where中会添加这个值是不是和原来不一样了

实体之间关联

1、VS自己生成

最简单的方法就是先把数据库创建好,主外键关系设置好,然后VS建一个项目,添加组件  ADO.NET实体数据模型。选择来自数据库的CodeFirst。这样所有的实体都创建好了。(自己框架里面自己写的一个工具,还可以生成注释的。)

2、关系介绍

1......*

0,1........*

*........*

  多对多要有中间表的中间表也要有主键,住的注意的是,EF中所有的表都要有主键。

自关联

实体组合,拆分

实体集合属性

public Class()
{
Student = new HashSet<Student>();
}
public virtual ICollection<Student> Student { get; set; }

public virtual ICollection<Student> Student { get; set; }被初始化为一个HashSet。编译的时候EF会把他编译为一个代理对象,变成Student类的子类,这个子类在内部封装了EF早期版本所开发的相应类型。因此,对Student属性值的改变会影响到实体对象的状态。

本文代码下载

EF-初识的更多相关文章

  1. ORM之EF初识

    之前有写过ef的codefirst,今天来更进一步认识EF! 一:EF的初步认识 ORM(Object Relational Mapping):对象关系映射,其实就是一种对数据访问的封装.主要实现流程 ...

  2. %E3%80%90%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E3%80%91

    "%3Cdiv%20class%3D%22htmledit_views%22%20id%3D%22content_views%22%3E%0A%20%20%20%20%20%20%20%20 ...

  3. Cookies 初识 Dotnetspider EF 6.x、EF Core实现dynamic动态查询和EF Core注入多个上下文实例池你知道有什么问题? EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    Cookies   1.创建HttpCookies Cookie=new HttpCookies("CookieName");2.添加内容Cookie.Values.Add(&qu ...

  4. EF Code First学习笔记 初识Code First

    Code First是Entity Framework提供的一种新的编程模型.通过Code First我们可以在还没有建立数据库的情况下就开始编码,然后通过代码来生成数据库. 下面通过一个简单的示例来 ...

  5. 初识EF

    1. EF是Entity Framework的缩写,全称是(ADO.Net Entity Framework),是以ADO.Net为基础所发展出来的对象关系对应(O/R Mapping)解决方案,早起 ...

  6. EF Code First学习笔记 初识Code First(转)

    Code First是Entity Framework提供的一种新的编程模型.通过Code First我们可以在还没有建立数据库的情况下就开始编码,然后通过代码来生成数据库. 下面通过一个简单的示例来 ...

  7. EF CodeFirst 初识

    随着EntityFramework的发展,原先的三种方式,{Code First ,Model First,Database First }  CodeFirst基本摆脱了另外两种方式 成为了 最受欢 ...

  8. MongoDB【第一篇】MongodDB初识

    NoSQL介绍 一.NoSQL简介 NoSQL,全称是”Not Only Sql”,指的是非关系型的数据库. 非关系型数据库主要有这些特点:非关系型的.分布式的.开源的.水平可扩展的. 原始的目的是为 ...

  9. [ActiveMQ]初识ActiveMQ

    初识ActiveMQ ActiveMQ介绍 官方网站:http://activemq.apache.org/ 最新版本:ActiveMQ 5.14.1(2016-10-28) 最新版本下载链接:htt ...

  10. 解读ASP.NET 5 & MVC6系列(2):初识项目

    初识项目 打开VS2015,创建Web项目,选择ASP.NET Web Application,在弹出的窗口里选择ASP.NET 5 Website模板创建项目,图示如下: 我们可以看到,此时Web ...

随机推荐

  1. 代码 | 自适应大邻域搜索系列之(7) - 局部搜索LocalSearch的代码解析

    前言 好了小伙伴们我们又见面了,咳咳没错还是我.不知道你萌接连被这么多篇代码文章刷屏是什么感受,不过,酸爽归酸爽.今天咱们依然讲代码哈~不过今天讲的依然很简单,关于局部搜索LocalSearch的代码 ...

  2. 代码 | 自适应大邻域搜索系列之(6) - 判断接受准则SimulatedAnnealing的代码解析

    前言 前面三篇文章对大家来说应该很简单吧?不过轻松了这么久,今天再来看点刺激的.关于判断接受准则的代码.其实,判断接受准则有很多种,效果也因代码而异.今天介绍的是模拟退火的判断接受准则.那么,相关的原 ...

  3. python下载后出现python 已停止工作

    背景: 在执行IDLE或者在terminal窗口执行 python命令时出现如下提示,修改了防火墙关闭也不行,找不到解决办法? 如图: [解决方案] 1.卸载重装python,确保python版本与系 ...

  4. geometry_msgs.msg.PoseStamped 代码示例

    https://programtalk.com/python-examples/geometry_msgs.msg.PoseStamped/

  5. Codeforces - 2019年11月补题汇总

    大概目标是补到 #500 为止的 Div. 2 ,先定个小目标,寒假开始前补到 #560 为止 Codeforces Round #599 (Div. 2) 5/6 备注:0-1BFS(补图连通块) ...

  6. 2019CSP-S初赛知识点汇总

    0x00 基本算法 0x01 位运算 0x02 前缀和与差分 0x03 二分 0x04 倍增 0x05 排序 0x06 离散化 0x07 高精度 0x10 数据结构 0x11 栈和队列 0x12 链表 ...

  7. Linux CentOS 使用Yum源安装MySQL 5.7

    在CentOS(Fedora.RedHat)系统中,可以使用yum install mysql命令来安装MySQL,但所安装的MySql版本一般都较旧,所以更推荐通过源码编译安装或下载最新rpm安装包 ...

  8. rpm包和deb分别是什么?

    一.RMP 是 LINUX 下的一种软件的可执行程序,你只要安装它就可以了.这种软件安装包通常是一个RPM包(Redhat Linux Packet Manager,就是Redhat的包管理器),后缀 ...

  9. 【Oracle/Java】向三张表各插入百万数据,共用时18分3秒,平均每张表6分钟

    三张表DDL如下: CREATE TABLE tb01 ( "ID" ,) not null primary key, "NAME" NVARCHAR2() n ...

  10. SQL-W3School-函数:SQL GROUP BY 语句

    ylbtech-SQL-W3School-函数:SQL GROUP BY 语句 1.返回顶部 1. 合计函数 (比如 SUM) 常常需要添加 GROUP BY 语句. GROUP BY 语句 GROU ...