【AaronYang风格】第一篇 CodeFirst 初恋
原著:Prorgamming Entity Framework Entitywork Code First
大家好!
我是AaronYang,这本书我也挺喜欢的,看了一半了,今晚也没事情,就分享一下一点东西吧
这本书总共8章,192页,整体内容看起来也不算吃力的。但是自己认真学的,感觉内容也不多。初学者完全不用害怕自己会不会学会,放心吧,会学会的。
虽然本书还是基于vs2010 开发的,但是我相信学会这个vs2012那个也会很简单,再说2010现在也还是主流(但也会对比vs2012的)。当然EF会采用版本EF5的
内容:Code First介绍,几种EF编程方式的对比,基本DEMO,Code First生成的数据库在哪,NuGet在EF中的使用,Migration的问题,其他错误处理,Code First 的简单Convention,配置示例等等,包学会的,亲!!!
读者约定:
Code First单词太长了,我简称 CF ,以后 我一直都会这样说哈,不是 腾讯的那个哈。
微软的ADO.NET EntityFramework,简称就是大家所说的EF,一个ORM框架。我对ORM的理解就是,按它的语法简化数据库的操作,平时都是写SQL什么的,ORM还有很多,例如NHibernate,Subsonic其他的等等,这里我不想讨论这个
在Code First出现之前的Model
EF第一次是在VS2008 .NET3.5中 被引进来的。当时工程师们可以用它反向地将数据库变成一个EDMX后缀名的一个实际上是一个XML的文件。它是可视化的,你可以再次调整你的model去作为你的Domain(三层的朋友的大多都是DAL,其实有的架构模式,其中的Domain类似于三层中的DAL层)。Visual Studio 2010 和 .NET4 又引进来了 EF的第二个版本,你们说的EF4(跟.NET版本一样,都是4,同样地,
.NET5,现在已经有EF5了,那么是不是应该学新的呢,我不清楚,如果有技术洁癖的朋友,自己调整一下),在动态创建模型(modeling)的 那一边,一个新的叫Model First的编程方式被添加进来了。从那以后,你就可以通过可视化界面(edmx文件双击后在vs2010里面会有界面,就是那个),你可以在那里面设计(字段,关系等等),然后可以基于你设计的model重新生成数据库(database)
Model First让开发者,首先设计模型,在进程中创建数据库,也就是动态创建数据库啦。这样子,你项目运行的时候,使用者,没有数据库也能动态创建。
不管你是否通过database-first还是model-first的方式设计了EDMX,这接下来的一步就是让你的domain层 里面,通过这些model中的实体和关系自动地创建类,生成代码,到这里开始,开发者就有强类型的类去表现他们的domain中的对象了。有了他们,你就可以很轻松的去操作数据库了。
还好在.NET4 中,EF有了另一个转变。以前在NET 3.5中,EF管理内存中的对象,它必须要让类继承Entity Framework的EntityObject对象。EntityObject把对象(谁继承EntityObject的类)的变化传递给Entity Framework,从而也就是变成了 跟踪这些 对象的变化,最后将这些变化持久化到数据库中去。在.NET4中,除了这个以外,还有个POCO(Plain Old CLR Object)支持,它可以让EntityFramework跟踪些简单的类,而不需要使用EntityObject。这样可以让开发者们使用他们自己的类,而不依赖于EntityFramework的了。因为EF 运行时候在内存中会用另一个自己的方式去跟踪和维护 对象的状态。
Code First 到来
在EF4的基础上,微软再一次地提供了一种方式去建模(modeling),而且现在已经有很多开发者在使用了----Code First.
Code First允许你 不再使用基于XML的EDMX文件去定义你的domain model
即使Model First和Database First可以让一些开发者使用他们(技术工具)生成的代码去解决处理问题,但是许多开发者既不想简单地使用设计器去处理问题,也不想使用它们生成的类去处理问题,那些人就是想去写代码去解决问题。哎~~
所以Code First可以满足那些人
在Code First中,你可以通过使用POCO类开始定义你的domain model,这些类都不依赖于Entity Framework的。Code First可以完全根据你定义的类的结构去推算出一些信息。你也可以进一步地提供一些额外的配置去描述你的model或者重写CF推断的依据的东西。这些配置也是通过code(写代码)配置的,不是XML文件或者设计器。
AaronYang 提醒
当你在使用designer(以前的EF有EDMX文件,双击是一个设计器,designer)的时候,EF4已经支持POCO类了。EF团队提供了 可以帮你生成POCO风格类代码的 POCO模版。这些类在你在设计器里面操作,做改变的时候,这些生成的类也会自动更新。你可能使用自己POCO类而不是使用工具帮你生成的类。但是如果你这样做的话,你必须保持类与EDMX同步。这就意味着,设计器改了,你的类你就要手动去改。Code First有个好处,你的类就是Model了,不需要设计器了,这就意味着,你要做改变什么的,你只要改一处,就是改你的 POCO类。
Code First,Database First和Model First都只是生成一些 符合Entity Framework去操作数据 特定规范的类的技术实现方式。一旦有了符合EF的
Model,EF不管你采用什么方式,哪一种技术,运行的时候表现的都是一样的。所以不管选择哪一种都是取决于你自己。下面一张图1-1,简单地说明了一些EntityFramework的细节
Code First在.NET4的时候,没有立刻发布,但是也没有在.NET5的时候带给开发者,在2011年4月,微软在发行EntityFramework4.1的时候,顺带把Code First一部分带给开发者了,当时还没有把Code First的核心部分发布。接下来,2011年10月 EntityFramework4.2发布了,替代了EF4.1,才真正包含了Code First。这时候,Entity Framework API的核心,System.Data.Entity.dll,已经作为.NET Framework中的一部分。这个dll在EF4.1和4.2是一样,没有变化。
Entity Framework4.1也包括了另一个很重要的特征(feature),叫做 DbContext API
DbContext是这个API的核心,当然核心还包括了其他的类。DbContext是Entity Framework中ObjectContext的轻量级版本。换句话说,ObjectContext是DbContext的超集,它包括了微软觉得开发者在使用EF时候经常使用的一些特征
DbContext可以让你用更简洁的方式,而不需要使用复杂的ObjectContext的编码语法,写更少的代码去实现很多通用的相同的功能。当使用Code First的时候,这一切更容易发现,通过这个系列的博客,你会慢慢体会到的。
还有本书叫 Programming Entity Framework: DbContext 在讲解DbContext,DbSet,验证API,还有一些其他的DbContext特点的内容的时候,会讲的更深点。
通过这幅图,发现Code First,DbContext是建立在EF4前提上的
开始写 Code First
Code First名字取的很巧妙哈,先写代码,再写其他的。我们先不考虑一些你可能需要用到的场景,我们先大致看下基本功能。这本书剩下的内容都会一直讲这个。
AaronYang 说明
在第一章里面,我们不需要示例代码。接下来写的一些代码只是简单的例子,不是贯穿全文的示例。从第二章开始,你将会实战练习。你可以用visual Studio 跟着写练习,你也可以尝试不看代码,自己写一下。
当然了,第一件事情,我们需要写一些类去描述我们的 商业领域(domain)。这个例子比较小,就是个 Patient 和 看兽医的Visit 访问记录
我们新建一个控制台程序吧
添加一个类Patient.cs,代码如下
using System;
using System.Collections.Generic;
namespace ChapterOneProject
{
public class Patient
{
public Patient()
{
Visits = new List<Visit>();
}
public int Id { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public AnimalType AnimalType { get; set; }
public DateTime FirstVisit { get; set; }
public List<Visit> Visits { get; set; }
}
public class Visit
{
public int Id { get; set; }
public DateTime Date { get; set; }
public String ReasonForVisit { get; set; }
public String Outcome { get; set; }
public Decimal Weight { get; set; }
public int PatientId { get; set; }
}
public class AnimalType
{
public int Id { get; set; }
public string TypeName { get; set; }
}
}
Code First核心原则----定义一个 让Code First基于你的类去生成模型的规则。比如说,EF要求一个类要有一个key属性(主键)。CF就会用一个规则,比如说属性名是否叫 Id,或者组合起来的Id(比如说 PatientId),像这类属性的名字都会自动地被CF理解为Key。如果按照规则找不到这样的属性,它会在运行的时候抛出一个异常,说没有key。其他一些的转换规则(convention),决定了string类型的默认长度,或者当你的类继承了其他的类,也就是子类没有东西,CF会按照约定给予默认的表结构,CF根据EF可以预测出来的。
如果CF仅仅依赖这点规则去处理你的类,肯定是有限制的。但是CF没有强加要求去怎么设计去满足它的处理规则。反过来想想,这些规则已经可以处理一批常见的场合问题了。如果你的类整合遵循这些规则,CF就不需要你再提供其他的配置了。EF将会直接处理你的类。如果你的类没有遵循约定,你就需要提供其他的配置来保证CF可以正确的理解你的设计。
在这3个类中,Id根据约定,都会是主键(key).我们先不添加其他配置,让CF处理这些类。
用DbContext管理Object
上面的这3个类对Entity Framework来说没有用,它不认识他们.
但是使用CF就可以了.你就可以使用你自己的类了.如果你使用其他项目的domain层的类,也就是自己的类了,CF将会非常有用了.
为了使用CF,你必须先定义一个类,它是要继承DbContext的.这个是这些类的角色之一,它会作为一个context(上下文),有了它,CF就知道怎样去构造你的model了。同样地,EF也是会理解的,从而跟踪它们的状态。这些完成都是靠一个新的类 DbSet。DbSet跟DbContext出生意义差不多。DbContext被包括在ObjectContext中,轻量级的。同样的,DbSet被包括在EF4的ObjectSet中,轻量级的,使用DbSet可以写更少的代码完成一样的任务。
下面的代码展示了context的大致样子。里面有DbSet泛型的Patients和Visits,但是没有AnimalTypes。CF足够的智能,它知道Patient会使用到AnimalType类,因此,它也是被包括在这个model中的。通过定义了DbSet,就可以很方便的查询数据了。
使用NuGet添加对 EntityFramework.dll的引用
或者通过这种方式添加对EntityFramework.dll的引用
输入 Install-Package EntityFramework 然后 回车(下面一节续篇 我们稍微讲下这个控制台的使用)
代码如下
using System.Data.Entity;
namespace ChapterOneProject
{
public class VetContext : DbContext
{
public DbSet<Patient> Patients { get; set; }
public DbSet<Visit> Visits { get; set; }
}
}
接下来,我们完全可以利用CF代码去操作了,它现在已经可以作为我们的数据访问层了,是不是有点吃惊。这里没有数据库连接字符串,甚至不存在数据库。但是数据访问层已经准备好了,你已经可以使用了。
打开Program.cs我们添加如下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ChapterOneProject
{
class Program
{
static void Main(string[] args)
{
var dog = new AnimalType { TypeName = "Dog" };
var patient = new Patient
{
Name = "Sampson",
BirthDate = new DateTime(2008, 1, 28),
AnimalType = dog,
FirstVisit = new DateTime(2008, 1, 28),
Visits = new List<Visit>
{
new Visit
{
Date = new DateTime(2011, 9, 1)
}
}
};
using (var context = new VetContext())
{
context.Patients.Add(patient);
context.SaveChanges();
}
Console.WriteLine("录入成功!");
Console.ReadKey();
}
}
}
创建一个动物类型,狗
然后创建一个病的狗,来看兽医是在2011年9月1日,就一次记录,还有其他关于狗的信息
接下来我们是CF语法把这条狗的病情和记录存到数据库中去
运行项目!!
数据库生成的位置
只要你的电脑装了 VS2012(不在乎你的电脑是否装了vs2010),默认CodeFirst配置生成数据库位置都会在localdb
VS2012还有种方式查看数据库SQL Server对象资源管理器
如果你的电脑只装了VS2010,如果你装了SQLExpress默认在 SQLExpress上
EF CodeFirst会根据你使用VS2010开发还是VS2012开发
会生成稍微不同的配置文件
我现在的电脑上装的是VS2010和2012都有,生成的App.config如下
上面基本一样的
注意下面的entityFramework那个节点
这里默认的是LocalDbConnectionFactory.EntityFramework,所以这个项目默认生成的数据库在LocalDb上
如果想生成在最常见的那种SQLServer上,你可以entityFramework节点下的内容改下
方式一:
亲,你现在还陌生吗?同样的不想使用sqlexpress,你自己换成其他的数据库连接字符串方式就行了
方式二:
加一个ConnectionStrings的节点
关于packages.config配置文件,我画红圈的地方不一样,看你使用的.netframework,vs2012应该是net45
如果需要SQLExpress 2008 R2的伙伴,可以到这里官网 下载
AaronYang 提醒
Code First中 Convention这个单词
Convention 这个单词我个人理解是 规定,约定。 写CodeFirst的人,如果你的类符合Convention,CodeFirst就能认识,然后它就能够按照已经定义好的Convention去处理你的类,生成你想要的数据库结构,然后你就可以利用EntityFramework操作数据库了。比如说Visit类中的PatientId符合Convention,这里自动处理成外键了。int类型,是其他类的名字+Id
再比如说,VetContext中没有声明DbSet<AnimalType>但是CodeFirst也会帮我们生成,它好像什么都知道的样子原因是你写的Code符合它的Convention了。还有很多其他的Convention,我们后面几章讲解到的。
后面的内容,我会经常说到Convention,到时候,我可不解释咯。如果想要具体添加或者修改Convention,我们可以通过配置完成,当然这里肯定不是DatabaseFirst或者ModelFirst中的 designer可视化操实际上XML文件的EDMX文件。具体的后面说吧
各种方式是如何把Class们变成数据库的,如图
这个就好比 用VC++,VB,C#等.net语言,不管前面怎么弄,最终会变成中间代码(IL)才能被机器识别
也就是说DataBase First, Model Fitst,Code First 最终都是变成内存中的对象,EntityFramework能够识别的(例如EntityType,AssociatonType),然后就可以跟踪,持久化数据操作等等。
简单的配置了解
Code First主要有两种,第一种是Data Annotations, 第二种是Fluent API
第二种的方式的配置更强大点,也就是说,DataAnnotations的功能是Fluent API功能的子集,但是DataAnnotations使用起来更简洁
======Data Annotations小例子====
我们应该发现AnimalTypes那张表中,可以为空,我们不希望为空,怎么做?
我们只要在TypeName属性上加一个Required
此时再次 运行项目,
会发生这个错误。因为模型改变了,建议我使用Code First Migrations去更新数据库。这里讲到了Migrations(迁移)
我们先解决这个问题
我们打开 包资源管理器控制台,输入enable-Migrations 大小写无所谓
运行后会生成一个文件夹,后面的20130829020525什么的是时间戳,让每次的Migration都不会出现重复的文件名
如果 你还遇到一个这样的问题(我是遇到了):
比如我尝试重新安装EntityFramework,因为我一开项目在家里的电脑的vs,然后用的公司电脑的vs,两个vs的NuGet版本
不一样,所以会报如下的错误,你只要重新安装最新版本的NuGet,就是保证两个前一次使用的NuGet版本和现在的要一样就行了
接下来,我们执行 add-migration AddRequiredOnAnimalType_TypeName(包管理控制台中执行)
添加一次迁移
然后我们执行update-database
刷新数据库,TypeName变成not null了,说白了每次的update-database命令都是转换成sql去执行、在后面 update-database –verbose 就可以查看这次迁移的sql语句了。
加了Required后就像ASP.NET MVC3中那样,Model中的属性就具有验证的功能了,如果为空,EF就会抛出异常的
由于客户机器上没有vs,所以你可以把这段sql拷过去,作为本次版本数据库的改变了
我们再在 AnimalType上加上Table注解,里面就是最新的表名称
我再做一次迁移 复习一下过程
1. add-migration UpdateAnimalTypeTableNameToSpecies
2. update-database –verbose
======Fluent API小例子====
同样的过程,我们使用Fluent API操作下
打开VetContext类,重写OnModelCreating方法,添加一些代码,估计一看就能看懂了,这里不细说了
using System.Data.Entity;
namespace ChapterOneProject
{public class VetContext : DbContext{public DbSet<Patient> Patients { get; set; }
public DbSet<Visit> Visits { get; set; }
protected override void OnModelCreating (DbModelBuilder modelBuilder){modelBuilder.Entity<AnimalType>().ToTable("Species");
modelBuilder.Entity<AnimalType>().Property(p => p.TypeName).IsRequired();}}}
判断开发者使用了那种EF模式,下面有张图判断
这里不做具体讨论了
最后留言
关于这本书,重点是在讲使用Code First构建和配置模型(Model).是 Programming Entity Framework (second edition)这本书的拓展。还有一本书叫Programming Entity Framework: DbContext,主要重点在DbContext, DbSet, Validation API,还有些EntityFramework NuGet 包的一些功能的使用讲解
代码下载: 下载
注意,可能我们的NuGet的包不太一样,所以运行时候可能有错误,所以代码仅供参考.如果有疑问,亲留言….
本文章是 Http://AaronYang.cnblogs.com AaronYang(杨洋)辛苦写的,转载时候,请标明出处