ORM(Object Relational Mapping)
要想学习EF框架,就需要知道什么ORM,ORM中文名叫对象关系映射。
我们来看一下定义:用于实现面向对象编程语言里不同类型系统的数据之间的转换。
emmm,感觉还不是很明确,简单的来说,ORM提供了实现数据层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。
ORM的方法论基于三个核心原则:
- 简单:以最基本的形式建模数据。
- 传达性:数据库结构被任何人都能理解的语言文档化。
- 精确性:基于数据模型创建正确标准化了的结构。
让我们从O/R开始。O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,而这些代码写起来总是重复的。
ORM解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。
总结一下ORM的特点:
提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。
什么是EF?
实体框架EF(Entity Framework) 是 ADO.NET 中的一组支持开发面向数据的软件应用程序的技术,是微软的一个ORM框架。
就是说,EF是基于ADO.NET的,一种使用ORM技术的框架。
从上图可以看出,EF框架在底层是通过调用ADO.NET来实现数据库操作的。
多转一道弯性能和灵活性肯定会受到影响,所以本文最后也会有ADO.NET教程链接,各位看过以后再比较EF和ADO.NET的优劣之处。
EF 的第一个功能就是建立一个实体数据模型(EDM),主要包括概念模型,存储模型以及概念和存储之间的映射。EF使用此EDM执行CRUD操作。它使用EDM从LINQ查询构建SQL查询,构建INSERT,UPDATE和DELETE命令,将数据库结果转换为实体对象。
查询
EF 使用EDM将LINQ-to-Entities查询转换为关系数据库的SQL查询,并将结果转换回实体对象。
保存
当调用SaveChanges()方法时,EF根据实体的状态推断INSERT,UPDATE和DELETE命令。ChangeTrack跟踪每个实体的状态,以及何时执行任何操作。
EF代码实操
在数据库中新建两张表
我在Winform程序中创建ADO.NET实体数据模型
然后一直点击下一步,记得看一下数据库中要用的表有没有被选上,一般默认是全选上的。
自动生成好以后是长这样的,我们着重看一下画红框的这两个类
先来看一下实体类,这就是普通的实体类嘛,没有什么区别,顺便讲一下 “?” 表示可空类型
public partial class Student
{
public int F_id { get; set; }
public string F_name { get; set; }
public int? F_age { get; set; }
public string F_hobby { get; set; }
}
public partial class Subject
{
public int F_id { get; set; }
public int F_stuId { get; set; }
public string F_subject { get; set; }
public decimal? F_score { get; set; }
}
DbContext
DbContext是EntityFramework很重要的部分,连接域模型与数据库的桥梁,是与数据库通信的主要类。
主要负责以下活动:
- EntitySet::DbContext包含了所有映射到表的entities
- Querying:将Linq-To-Entities转译为Sql并发送到数据库
- Change Tracking:从数据库获取entities后保留并跟踪实体数据变化
- Persisting Data:根据entity状态执行Insert、update、delete命令
- Caching:DbContext的默认第一级缓存,在上下文中的生命周期中存储entity
- Manage Relationship:DbContext在DbFirst模式中使用CSDL、MSL、SSDL管理对象关系,Code first中使用fluent api 管理关系
- Object Materialization:DbContext将物理表转成entity实例对象
那么我们具体来看一下Context这个类,这个类的作用主要是连接数据库和映射数据表,所以每次想要增加一张新表的时候我们需要在这个类中加一个DbSet的语句来通知EF映射表关系。
//他是一个抽象类,继承了DbContext
public partial class TestDemoEntities : DbContext
{
//构造方法,base的意思是使用config文件中名为TestDemoEntities的连接字符串连接数据库
public TestDemoEntities() : base("name=TestDemoEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
//映射实体类和数据库中的物理表
public virtual DbSet<Student> Student { get; set; }
public virtual DbSet<Subject> Subject { get; set; }
}
CRUD
那么现在又到了老生常谈的CRUD问题了,既然EF是映射实体类和物理表,那怎么使用呢?来看代码
public partial class Form1 : Form
{
//实例化Context对象
TestDemoEntities db = new TestDemoEntities();
public Form1()
{
InitializeComponent();
}
//查找全部
private void btn_query_Click(object sender, EventArgs e)
{
//查询所有数据放到list中
List<Student> list = db.Student.ToList();
textBox1.Text = "序号\t姓名\t年龄\t爱好\r\n";
foreach(Student stu in list)
{
textBox1.Text += stu.F_id + "\t" + stu.F_name + "\t" + stu.F_age + "\t" + stu.F_hobby + "\r\n";
}
}
//增加数据
private void btn_insert_Click(object sender, EventArgs e)
{
//通过Add方法将实例化的Student对象中的属性放到Student实体对象中
db.Student.Add(new Student() {F_name="阿强", F_age=33, F_hobby="游泳" });
//执行增删改方法必须执行SaveChanges()方法,目的将执行结果保存到数据库,返回影响的行数
int result = db.SaveChanges();
if (result > 0)
MessageBox.Show("添加成功!");
else
MessageBox.Show("添加失败!");
}
//修改数据
private void btn_edit_Click(object sender, EventArgs e)
{
//先根据id取出数据库中这条记录放到实体对象中
var student = db.Student.FirstOrDefault(s => s.F_id == 2);
//修改实体对象的属性值
student.F_hobby = "跳绳";
//保存
int result = db.SaveChanges();
if (result > 0)
MessageBox.Show("修改成功!");
else
MessageBox.Show("修改失败!");
}
//删除数据
private void btn_delete_Click(object sender, EventArgs e)
{
//定义一个实体对象,将id设置为要删除的id值
Student stu = new Student() { F_id = 2};
//将对象附加到上下文中
db.Student.Attach(stu);
//删除该对象
db.Student.Remove(stu);
//保存
int result = db.SaveChanges();
if (result > 0)
MessageBox.Show("删除成功!");
else
MessageBox.Show("删除失败!");
}
}
当然,上述实现了基本的增删改查操作,但是绝对不可能解决所有的情况,具体解决方式要根据具体情况进行分析,下面我再写几个常见的情况。
只查找一条记录
Student stu = db.Student.Where(s => s.F_id == 1).FirstOrDefault();
根据条件查找列表
List<Student> list = db.Student.Where(s => s.F_age > 18).ToList();
批量删除
List<Student> list = db.Student.Where(s => s.F_age > 18).ToList();
if(list != null && list.Any())
{
foreach(Student stu in list)
{
db.Student.Remove(stu);
}
}
总结
综上所述,发现EF其实也就是操作数据库的一个中间组件,与ADO.NET的功能一样,而且是基于ADO.NET的,性能上肯定会比ADO.NET差(大数据量才会体现),那么为什么还要用EF呢?
个人认为,就是两个字,简便。用EF的话大家会发现与ADO.NET相比代码简洁了不少,没有了SqlHelper,也不用写sql语句了,结合Lambda表达式很轻松地就可以操作数据库,但是EF的灵活性和性能的确没有ADO.NET好,所以实际开发我们一般结合EF和ADO.NET混合开发。
一项技术的出现必然使得代码走向简洁,别觉得会一种技术就高枕无忧不去学习其他类似技术了,技术本身不会有好坏之分,只是使用场景不同技术的优劣就会体现,说白了,没有最好的,只有最合适的。
想了解ADO.NET的童鞋可以看我另一个教程 ADO.NET详解