Entity Framework简介
Entity Framework是微软的Object Relational Mapper(对象关系映射器),也就是我们平常说的ORM,它可以让应用程序开发者将关系型数据作为业务模型来使用,也消除了开发者为数据访问编写的绝大多数管道代码的需要(比如使用ADO.NET)。Entity Framework提供了一个综合的、基于模型的系统,通过摆脱为所有的领域模型编写相似的数据访问代码,使得开发者创建数据访问层是如此之简单。
Entity Framework通过开启数据访问和将数据表示为概念化模型(即一系列的实体类和关系)减轻了创建数据访问层的任务。应用程序可以执行基本的CRUD操作,以及轻松地管理实体间的一对一,一对多和多对多关系。
下面是使用Entity Framework的一些好处:
1.因为开发者不需要为数据访问编写所有需要的ADO.NET管道代码,因此这可以节省很多开发时间。
2.我们可以使用更高级的语言(例如C#)来编写所有的数据访问逻辑而不是编写SQL查询和存储过程。
3.因为数据库表没有高级的关系(如继承),然而领域实体是可以有的,所以业务模型(也就是概念模型)可以使用实体间的关系来适配应用领域。
4.底层的数据存储可以相对轻松地被取代,因为所有的数据访问逻辑都呈现在应用层而不是数据层。
Entity Framework的架构
EF构建在provider架构之上。当开发者使用C#创建一个LINQ查询时,EF框架引擎会连接一个provider,将它转换成实际的SQL语句,最后发往数据库。任何给定的provider都是连接Entity Framework和一个特定的RDBMS的桥梁。一旦该provider执行了最终的SQL命令,结果就被EF物质化到.NET对象中。Data reader就是为了这个目的。理解EF构建于ADO.NET之上非常重要,因此,EF也使用了诸如connection,command,和data reader的概念。谈到数据持久化,也就是插入,更新和删除功能,插入时,开发者将一个实体类的实例添加到数据库上下文中。相似地,之前添加到上下文中的实例被标记为changed或deleted,就会产生对数据库即将执行更新和删除的语句。EF会检查上下文中的每个对象,再次使用provider来创建RDBMS特定的insert,update,或delete命令。
Entity Framework建模和持久化
EF依赖概念模型完成工作,首先来理解一下什么是Entity Data Model(EDM)以及EF如何使用它管理数据库操作。
理解EDM
概念数据模型是EF的核心。要使用EF,我们必须创建概念数据模型,即EDM。EDM定义了我们的概念模型类,这些类之间的关系,以及这些模型到数据库模式之间的映射。
一旦创建了EDM,我们就可以对概念模型执行所有的CRUD操作,EF会将所有的这些对象查询翻译成数据库查询(SQL)。一旦这些查询执行了,EF就会将返回的结果转成概念模型对象实例。EF会使用存储在EDM中的映射信息来执行对象查询到SQL查询,以及相关的数据到概念模型的翻译。
一旦EDM准备就绪,我们就可以使用模型对象来执行CRUD操作。要能够执行CRUD操作,我们必须使用ObjectContext类。接下来让我们理解一下这个类。
理解ObjectContext类
一旦我创建了EDM,我就有了应用程序中可以使用的所有的实体。然而,我还需要一个东西来让我在这些实体上执行各种操作。它就是EF中的ObjectContext类。
ObjectContext类是EF中的主要对象。它负责:
管理数据库连接
提供执行CRUD操作的支持
追踪模型的更改,目的在于在数据库中更新模型
ObjectContext类可以理解成管理EDM中所有实体的东西,让我们为这些实体执行所有的数据库操作。当我们想要保存一个新的或者更改的对象到数据库时,我们必须调用ObjectContext类中的SaveChanges方法。
还有另一个类DbContext,它和ObjectContext类很相似。实际上,Dbcontext类就是ObjectContext类的封装类。它是一个更新的API,而且它提供了更好的API来管理数据库连接和执行CRUD操作。
因为DbContext是更好的API,所以我们会使用DbContext来执行所有的数据库操作。
Entity Framework的三种开发风格
Database First:这是一种用于已存在数据库模式的方法。使用这种方法,EDM是从数据库模式中生成的,这种方法最适合于使用了已经存在的数据库的应用。
笔者这里常用的是DBFirst,以开发者角度而言,笔者的工作模式是:拿到需求,进行页面模型设计,这一部分会和业务人员进行反复沟通最终确认,然后再到数据库表的设计,最后才是程序的开发。
在使用EF时,如果频繁操作表字段(增删改表字段),程序中引用的EF也要更改模型,可能会导致模型崩溃,出现无法预知的错过,需要卸载模型,重新添加,所以笔者不建议频繁的去更改数据库的表字段。
//创建EF时,自动生成的代码 //这里我们可以看到,已经生成自定义的上下文实体,继承自 ObjectContext public partial class BillForEntities : ObjectContext { #region 构造函数 /// <summary> /// 请使用应用程序配置文件的“BillForEntities ”部分中的连接字符串初始化新 BillForMMHSEEntities 对象。 /// </summary> public BillForEntities () : base("name=BillForEntities ", "BillForEntities") { this.ContextOptions.LazyLoadingEnabled = true; OnContextCreated(); } } //这样我们可以在某个类里通过实例化BillForEntities 来操作上下文 using (var db = new BillForEntities()) { foreach (SelectedRow item in sm.SelectedRows) { Guid id = Guid.Parse(item.RecordID); TB_ZNHSE_BILL_HEAD v = db.TB_ZNHSE_BILL_HEAD.FirstOrDefault(t1 => t1.BILL_ID == id); v.REMOVE_ABNORMAL_EXPLAIN = this.TextField1.Text; db.SaveChanges(); } }
Code First:这种方法中,所有的领域模型都是以类的形式编写的。这些类会建立我们的EDM,数据库模式会从这些类中创建。这种方法最适合于那些高度以领域为中心并且领域模型类创建优先的应用程序。这里需要的数据库只是为了这些领域模型的持久化机制。
Model First:这种方法和Code First方法很相似,但是这种情况下我们使用了EDM视觉设计器来设计我们的模型。数据库模式和类将会通过这个概念模型生成。该模型将会给我们创建数据库的SQL语句,然后我们可以使用它来创建数据库并连接应用程序。
EF实现增删查改
数据读取和使用方法 对于以下两个实体类Blog和BlogInfo: // 有2个实体分别是Blog和BlogInfo public class Blog { public int Id { get; set; } public Guid OwnerId { get; set; } public string Caption { get; set; } public DateTime DateCreated { get; set; } //一个Blog对一个Info public BlogInfo Info { get; set; } //一个Blog对多个Article public ICollection<BlogArticle> Article { get; set; } } public class BlogInfo { public Guid Id { get; set; } public string Information { get; set; } //一个Blog对一个Info public Blog Blog { get; set; } } 增加: using(BlogModel context=new BlogModel()) { context.Blog.Add(new Blog { Id = 1, Caption = "Customer #1", DateCreated = DateTime.Now, Info=new BlogInfo { Information="TryA" } }); context.SaveChanges; } 查询: using(BlogModel context=new BlogModel()) { //查多条 var q = from item in context.Blogs select; //查第一条 var blog = from b in context.Blogs where b.Caption.StartsWith("B") select b; var blog = context.Blogs .Where(b => b.Caption == "ADO.NET Blog") .FirstOrDefault(); // 会查找数据库中的数据 var blog = context.Blogs.Find(3); // 会返回实例中的数据而不是到数据库中查找 var blogAgain = context.Blogs.Find(3); context.Blogs.Add(new Blog { Id = -1 }); // 会查到数据库中不存在的这个new Blog数据 var newBlog = context.Blogs.Find(-1); // 会根据这个string查找User var user = context.Users.Find("johndoe1987"); } 修改: using(BlogModel context=new BlogModel()) { //查一条然后修改 var blog = (from b in context.Blogs where b.Caption.StartsWith("B") select b) .single(); blog.Caption = "Best"; context.SaveChanges; //根据Id修改 blog = new Blogs() { Id = 1,//会根据这个ID来修改 Caption = best }; //下面这条是blog实例全部属性都写全的时候用 context.Entry<Blogs>(blog).State = System.Data.Entity.EntityState.Modified; //下面这条是blog实例只写了Id和要改的部分 context.Entry<Blogs>(blog).Property<string>(b => b.Caption).IsModified = true; } 删除: using(BlogModel context=new BlogModel()) { //查一条然后删除数据读取和使用方法 对于以下两个实体类Blog和BlogInfo: // 有2个实体分别是Blog和BlogInfo public class Blog { public int Id { get; set; } public Guid OwnerId { get; set; } public string Caption { get; set; } public DateTime DateCreated { get; set; } //一个Blog对一个Info public BlogInfo Info { get; set; } //一个Blog对多个Article public ICollection<BlogArticle> Article { get; set; } } public class BlogInfo { public Guid Id { get; set; } public string Information { get; set; } //一个Blog对一个Info public Blog Blog { get; set; } } 增加: using(BlogModel context=new BlogModel()) { context.Blog.Add(new Blog { Id = 1, Caption = "Customer #1", DateCreated = DateTime.Now, Info=new BlogInfo { Information="TryA" } }); context.SaveChanges; } 查询: using(BlogModel context=new BlogModel()) { //查多条 var q = from item in context.Blogs select; //查第一条 var blog = from b in context.Blogs where b.Caption.StartsWith("B") select b; var blog = context.Blogs .Where(b => b.Caption == "ADO.NET Blog") .FirstOrDefault(); // 会查找数据库中的数据 var blog = context.Blogs.Find(3); // 会返回实例中的数据而不是到数据库中查找 var blogAgain = context.Blogs.Find(3); context.Blogs.Add(new Blog { Id = -1 }); // 会查到数据库中不存在的这个new Blog数据 var newBlog = context.Blogs.Find(-1); // 会根据这个string查找User var user = context.Users.Find("johndoe1987"); } 修改: using(BlogModel context=new BlogModel()) { //查一条然后修改 var blog = (from b in context.Blogs where b.Caption.StartsWith("B") select b) .single(); blog.Caption = "Best"; context.SaveChanges; //根据Id修改 blog = new Blogs() { Id = 1,//会根据这个ID来修改 Caption = best }; //下面这条是blog实例全部属性都写全的时候用 context.Entry<Blogs>(blog).State = System.Data.Entity.EntityState.Modified; //下面这条是blog实例只写了Id和要改的部分 context.Entry<Blogs>(blog).Property<string>(b => b.Caption).IsModified = true; } 删除: using(BlogModel context=new BlogModel()) { //查一条然后删除 var blog = (from b in context.Blogs where b.Name.StartsWith("B") select b) .single(); context.Blogs.Remove(blog); context.SaveChanges; var blog = (from b in context.Blogs where b.Name.StartsWith("B") select b) .single(); context.Blogs.Remove(blog); context.SaveChanges;