EF-初识
什么是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-初识的更多相关文章
- ORM之EF初识
之前有写过ef的codefirst,今天来更进一步认识EF! 一:EF的初步认识 ORM(Object Relational Mapping):对象关系映射,其实就是一种对数据访问的封装.主要实现流程 ...
- %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 ...
- 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 ...
- EF Code First学习笔记 初识Code First
Code First是Entity Framework提供的一种新的编程模型.通过Code First我们可以在还没有建立数据库的情况下就开始编码,然后通过代码来生成数据库. 下面通过一个简单的示例来 ...
- 初识EF
1. EF是Entity Framework的缩写,全称是(ADO.Net Entity Framework),是以ADO.Net为基础所发展出来的对象关系对应(O/R Mapping)解决方案,早起 ...
- EF Code First学习笔记 初识Code First(转)
Code First是Entity Framework提供的一种新的编程模型.通过Code First我们可以在还没有建立数据库的情况下就开始编码,然后通过代码来生成数据库. 下面通过一个简单的示例来 ...
- EF CodeFirst 初识
随着EntityFramework的发展,原先的三种方式,{Code First ,Model First,Database First } CodeFirst基本摆脱了另外两种方式 成为了 最受欢 ...
- MongoDB【第一篇】MongodDB初识
NoSQL介绍 一.NoSQL简介 NoSQL,全称是”Not Only Sql”,指的是非关系型的数据库. 非关系型数据库主要有这些特点:非关系型的.分布式的.开源的.水平可扩展的. 原始的目的是为 ...
- [ActiveMQ]初识ActiveMQ
初识ActiveMQ ActiveMQ介绍 官方网站:http://activemq.apache.org/ 最新版本:ActiveMQ 5.14.1(2016-10-28) 最新版本下载链接:htt ...
- 解读ASP.NET 5 & MVC6系列(2):初识项目
初识项目 打开VS2015,创建Web项目,选择ASP.NET Web Application,在弹出的窗口里选择ASP.NET 5 Website模板创建项目,图示如下: 我们可以看到,此时Web ...
随机推荐
- 使用开源软件 jumpserver 搭造自己的堡垒机
使用开源软件 jumpserver 搭造自己的堡垒机 开软地址:https://github.com/jumpserver/jumpserver 目前版本:1.5.2 测试的时候有少许BUG,但功能却 ...
- formidable处理提交的表单或图片文件的简单介绍
一般来说,客户端向服务端提交数据有GET和POST这两种方式,在之前的文章node.js当中的http模块与url模块的简单介绍当中我们可以知道通过req.url与url模块的配合处理可以快速得到客户 ...
- Poseidon 系统是一个日志搜索平台——认证看链接ppt,本质是索引的倒排列表和原始日志数据都存在HDFS,而文档和倒排的元数据都在NOSQL里,同时针对单个filed都使用了独立索引,使用MR来索引和搜索
Poseidon 系统是一个日志搜索平台,可以在百万亿条.100PB 大小的日志数据中快速分析和检索.360 公司是一个安全公司,在追踪 APT(高级持续威胁)事件,经常需要在海量的历史日志数据中检索 ...
- resin初识
Resin初识 1. resin简介 刚入职的公司用的后台服务器是resin,故因此学习记录一下. resin是一个非常流行的web引用服务器,对servlet和jsp提供了良好的支持,自身采用jav ...
- zabbix 定义触发器,并使用邮件,微信消息报警。
触发器可根据监控项获取到的值来进行一些操作,如监控项获取到的values为0,触发器可判断为正常,如果获取到了1,就触发报警. 定义报警方式比较简单,但是用shell脚本实现起来,总是有格式问题,所以 ...
- Flutter移动电商实战 --(48)详细页_详情和评论的切换
增加切换的效果,我们主要是修改这个地方 这样我们的评论的内容就显示出来了 最终代码 details_web.dart import 'package:flutter/material.dart'; i ...
- 小程序checkbox调整大小
.cb{ transform: scale(0.6,0.6); } <view> <label class="lab" for="box1"& ...
- java内存空间简述
JVM的内存空间: 1. 寄存器 (Registers):最快的保存区域,位于处理器内部,由编译器分配.主要作用是记录当前线程所执行的字节码的行号.字节码解释器工作时就是通过改变当前线程的程序计数器选 ...
- SVG-变换
transform变换 translate平移 <svg width="200" height="50"> <rect x="0&q ...
- 【分类算法】决策树(Decision Tree)
(注:本篇博文是对<统计学习方法>中决策树一章的归纳总结,下列的一些文字和图例均引自此书~) 决策树(decision tree)属于分类/回归方法.其具有可读性.可解释性.分类速度快等优 ...