零、前言
介于鄙人也是初试牛刀,对于此工具的认识也有局限性。如若有任何错误的地方,还请各位大牛指正。
本文大部分内容翻译自MSDN库,地址:http://msdn.microsoft.com/zh-cn/data/jj193542 ,其中还有一段视频,可惜是英文的,还没有字幕,英语好的同学可以传送过去瞅一眼~
一、Code First简介 [1]
ADO.NET Entity Framework 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) 解决方案,其中包括了三种使用方式:数据库优先(Database First),模型优先(Model First)和代码优先(Code First)。他们的关系图如下:
本文所要进行深入探讨的,就是三者中的Code First,其说明如下:不管你是否已经有数据库,你都可以编写自己的类和创建数据库或者将数据关联到数据表和字段,并且仅仅只需要数行代码而已,因此有时候这种方法又被称为 Code Only,当然经典的名称为 Code First。在数据库的存储架构到概念模型之间的映射通过约定以及特定的映射 API 完成。如果你还没有数据库,EF 可以自动为你创建它,在模型改变的时候,先删除掉然后重新创建。使用代码优先的方式进行数据库访问的 API 基于 DbContext 类。这也同样可以用于数据库优先或者模型优先的开发流程。
二、何时使用Code First?[2]
关于这个问题的答案,实际上取决于你创建,并存储你的C#/VB类库与数据库之间映射关系的个人习惯与喜好。如果你希望使用代码来完成这一切,那么Code First就是为你量身打造。并且Code First适用范围非常广,甚至你的数据库已经存在,或者被数据库管理员指定,你都可以使用Code First来完成对象关系映射的工作。但若你更倾向于使用可视化的设计器以及XML文件来完成这项工作,那么数据库优先(Database First)、模型优先(Model First)可能更合你的胃口。也就是说,Code First并没有功能上的局限性,仅仅只是取决于使用者的个人习惯而已。
三、Code First的第一个简单实例 [3]
- 毕竟是简单实例嘛,让我们从基本的控制台应用程序开始入手
l 打开Visual Studio
l 文件 -> 新建 -> 项目…
l 找到控制台应用程序
l 取个名字,鄙人觉得好玩就叫First Code First(记住这个名字,后面有所体现!)
l 按下确定就到位了
- 建立模型
我们用类库来定义一个非常简单的模型
public class Blog => Blog类,这在之后将默认作为表名
{
public int BlogId { get; set; } => BlogId与Name将是表的列
public string Name { get; set; }
public virtual List<Post> Posts { get; set; } =>这里的virtual是EF框架的延迟加载选项,这样做以后只有当你试图访问Posts的属性的时候,才会被加载
}
public class Post => 与之前的Blog类一样,不再赘述
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
- 建立映射关系
我们现在要开始让类模型与数据库产生关系,这里我们需要用到Entity Framework框架下的System.Data.Entity.DbContext命名空间内的DbSet<TEntity>类型,通过它来给之前建立的类创建数据库并关联起来。
l 项目 –> 管理 NuGet 程序包
l 找到 EntityFramework 程序包
l 点击 安装
完成了这一必要的准备工作后,我们在之前建立的类下面添加以下内容:
public class BloggingContext : DbContext =>DbContext是EF框架下的一个基本类型,他会向你呈现数据库会话并允许你通过来对数据库进行操作。
{
public DbSet<Blog> Blogs { get; set; } =>DbSet将允许你保存对应类型的对象实例
public DbSet<Post> Posts { get; set; }
}
若是缺少之前的Entity Framework程序包,那么上面的代码将不被识别,像这样:
即使在安装完以后,也需要引入以下命名空间,否则也无法识别:
using System.Data.Entity;
上面这步也完成以后,便顺利识别了:
4.数据的读与写
我们已经完成了使用Code First模式,完全重新建立一个新数据库的代码铺垫,接下来我们就要使用以上的代码,来真正创建数据库实例了。
我们在Main函数中,写入一些基础代码,来帮助我们达到测试效果:
static void Main(string[] args)
{
using (var db = new BloggingContext()) ß这里的using表示一个生存周期,当using内部的操作全部执行完毕之后,程序将自动释放括号内申明的变量,本例中为var db = newBloggingContext()
{
// 创建并保存博客
Console.Write("博客标题: ");
var name = Console.ReadLine();
var blog = new Blog { Name = name };
db.Blogs.Add(blog);
db.SaveChanges();
// 显示所有博文
var query = from b in db.Blogs
orderby b.Name
select b;
Console.WriteLine("全部博文:");
foreach (var item in query)
{
Console.WriteLine(item.Name);
}
Console.WriteLine("按任意键退出...");
Console.ReadKey();
}
}
接着尝试运行程序,首次输入博客标题后,因为要建立新的数据库实例,需等待较长时间,结果如下:
那么被创建的数据库在哪?至少鄙人找了一圈没找到……
这是一个非常有意义的问题,智能的Code First将会分两种情况来处理。
l 如果你已经安装了一个可用的本地SQL Express[4]实例(Visual Studio 2010默认安装项),那么Code First已经把数据库创建在这个实例中。
l 如果SQL Express并不可用,那么Code First将会尝试使用LocalDb[5](Visual Studio 2012默认安装项)
l 这个数据库的名称将是你的项目名称与数据类模型名称之和,在本例中为First_Code_First.BloggingContext
l 上面提到的情况,是默认的约定,当然还有其他很多的方式来更改Code First所用的数据库,若需了解这些信息请关注How DbContext Discovers the Model and Database Connection一文
接下来我们来演示,如何使用Visual Studio的Server Explorer(服务器资源管理器)功能来连接到目标数据库。
l 视图 =>服务器资源管理器
l 右键点击数据连接,并选择添加连接一项
l 如果你之前还没有通过Server Explorer连接到数据库,那么你将需要选择Microsoft SQL Server来做为数据源,如下图:
l 连接到LocalDb((localdb)\v11.0) 或者 SQL Express(.\SQLEXPRESS)其中的一个,这取决于你安装了其中的哪一个,在本例中,鄙人使用的是Visual Studio 2012,那么就是前者了,如下图:
l 按下确定,稍候片刻,你就能在服务器资源管理器中,看到之前由Code First建模,并运行创建的数据库实例。
l DbContext通过查询我们定义的DbSet属性,完成了类以至包括模型的计算,之后它使用Code First的默认约定,完成了表、列名、数据类型、定义主键等一系列创建数据库所包含的工作。到这里,Code First建模一事,已经完成,之后我们将讨论如何来自定义操作,而非使用默认的方式。
5.处理模型的改变
数据库结构设计修改,是我们不得不面对的一个问题,那么现在就让我们开始来着手处理数据库模型的设计更改问题。针对这样的问题,Code First也需要更新数据库模式,这将用到一个叫做Code First Migrations,或者Migrations for short的工具。
Migrations赋予一组有序的命令步骤,它描述了如何升级(降级)我们的数据库架构,其中的每一个步骤,称为迁移,其中包含了一些命令代码,他们描述了将要被执行的改变。
第一步,需要让我们的BloggingContext具备Code First Migrations的能力。
l 工具=>库程序包管理器à程序包管理器控制台
l 在程序包管理器控制台中,运行Enable-Migrations命令,如下图:
l 该命令执行完毕后,有一个名为Migrations的文件夹已经被添加到你的项目中,他包含两个文件:
. Configuration.cs – 这个文件包含了一些我们想要对BloggingContext进行迁移的设置,我们现在不进行对其更改的练习,但是这里是我们用来指定子数据,注册其他数据库连接,更改migrations的命名空间等等。
. <timestamp>_InitialCreate.cs – 这是你的第一次迁移信息,它将呈现你已经应用的数据库,包括Blogs和Posts表的创建语句。每一次的数据库修改记录,都会产生这样一个文件,并且保存在本地,由时间戳组成的文件名将帮助我们排序。
l 现在让我们来对我们的模型进行一些改变,给Blog类添加一个Url属性:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual List<Post> Posts { get; set; }
}
l 在类中添加属性后,到程序包管理器控制台中,运行Add-Migration AddUrl命令。
Add-Migration命令会检查从你最后一次数据库迁移,到现在的全部修改,并且根据这些改变,搭建一个新的迁移记录。我们可以给这个迁移记录命名,在本例中,我们使用了AddUrl来标识本次数据库结构迁移。
这些架构代码告诉数据库,我们需要给Blogs表添加一个新列,名为Url,数据类型为String。如果有必要,我们自己可以对代码进行修改,但是此例中并不必要。代码如下:
文件名:201212111258178_AddUrl.cs
内容:
namespace First_Code_First.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddUrl : DbMigration
{
public override void Up()
{
AddColumn("dbo.Blogs", "Url", c => c.String());
}
public override void Down()
{
DropColumn("dbo.Blogs", "Url");
}
}
}
l 需要注意的是,仅仅是创建数据迁移需求所需的命令,并不会对数据库产生实质的变化,因此我们还需要应用这些命令。
在程序包管理器控制台中运行Update-Database命令,它将会应用那些还没有被提交的数据库迁移记录。因为我们已经创建了数据迁移需求代码,所以程序会直接执行那些代码,来达到更新数据库结构的目的。
提示:当你想要看到更新数据库时,具体执行的SQL过程,可以使用 –Verbose 选项,来达到这个目的。刷新服务器资源浏览器,结果如下:
6.数据批注
到目前为止,我们只是让EF使用默认约定来处理模型,但是有时候,当我们的类模型并不能使用默认约定来处理的时候,我们需要进行进一步的设置。我们有两种选项来完成这样的需求,本节中将讨论数据批注,在下一节中,我们将使用犀利的API方式。
l 现在让我们来给模型添加一个User类:
public class User
{
public string Username { get; set; }
public string DisplayName { get; set; }
}
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; }
}
l 当我们试图添加一个迁移日志的时候,将发生一个错误,血红血红的文字表示说:“EntityType ‘User’ has no key defined. Define the key for this EntityType.”,大致意思是说实体类User缺少主键定义,必须给User实体类定义主键。
这是因为EF没有办法知道Username是否被被设置为User实体类的主键。
l 对此,我们将使用Data Annotations,并且这需要我们在项目顶端添加一个命名空间:
using System.ComponentModel.DataAnnotations;
l 然后指定Username来做为我们User类的主键:
public class User
{
[Key]
public string Username { get; set; }
public string DisplayName { get; set; }
}
l 这样做以后我们再进行Add-Migration AddUser命令,来搭建数据迁移脚本就不会报错。
l 之后再运行Updata-Datebase命来来应用数据迁移命令。
成功!结果如下:
7.犀利的API
在前面的章节中,我们看到了使用数据批注来补充,或者重写默认公约的方式,来达到自定义数据库模型的目的。那么另外一个配置模型的方法,就是使用犀利的API方法。
绝大多数数据模型的配置,都可以使用简单的数据批注来完成。那么犀利的API是一种制定模型配置更加高阶的方式,它完全涵盖了数据批注所能作的任何事情,并能做到数据批注所不可能做到的一些配置。值得一提的是,数据批注和犀利的API是可以一起使用的。
l 使用下面的代码来重写BloggingContext的OnModelCreating方法:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Property(u => u.DisplayName)
.HasColumnName("display_name");
}
}
l 然后使用Add-Migration ChangeDisplayName命令,来搭建改变数据库的迁移命令。
l 运行Update-Database命令,来应用新的数据迁移到数据库实例。
结果如下:
四、数据批注的更多选项
数据批注实际上包含了非常多的功能,下面将列出全部的标签,并附上其相关的说明。
l KeyAttribute [6]
表示唯一地识别一个实体的一个或多个属性
l StringLengthAttribute [7]
指定在一个数据字段中的字符被允许的最小和最大长度
l MaxLengthAttribute [8]
指定数组或字符串数据的属性中允许的最大长度
l ConcurrencyCheckAttribute [9]
指定属性参与到乐观性并发检测
l RequiredAttribute [10]
指定数据字段值是必需的
l TimestampAttribute [11]
指定数据类型的列行版本
l ComplexTypeAttribute [12]
表示这个类是一个复杂类型
l ColumnAttribute [13]
表示一个列属性
l TableAttribute [14]
指定类所要映射的数据库表
l InversePropertyAttribute [15]
将属性的表现形式取反
l ForeignKeyAttribute [16]
将一个属性指定为关系依赖中的外键
l DatabaseGeneratedAttribute [17]
表示一个数据库生成的属性
l NotMappedAttribute [18]
表示从数据库中映射的属性或类应该被排除在外
五、总结
使用Entity FrameWork-Code First,让我们从过去繁琐的建库、建表、建立连接、编写插入/更新/删除SQL命令等一系列工作中解放出来,轻松享受对象关系映射所带来的种种便利。值得一提的是,他不同于一般高效开发框架,通过阉割灵活性、自定义性来达到快速开发的目的,严重损害了程序本身的适用性、功能性与可拓展性。Code First不仅具有其易用的默认约定配置,他在这种基础上,添加了丰富的进阶配置功能,对使用的数据库版本有着非常高的自由度,从Visual Studio默认附带安装的SQL Server Express到大型的SQL Server企业版,并引入了数据批注与API两种方式,允许对数据库的表对象进行细致入微的自定义。
借助Migrations工具,我们可以自定义每一次数据库更新的迁移标签,与以往通过查询基于时间的数据库日志,来推算数据库更改相比,通过自己命名的标签来判断数据库进行的修改历史记录,大大提高了可视化程度与易操作性。
当然Code First必然也有其弱点,就目前而言,他还算是一种新兴技术,功能完备性与理论体系成熟性上,与当今深入人心的Oracle、MySQL、SQL Server等主流数据库体系仍有较大差距,而且从事这些成熟技术的专业人员数量相比,也有明显的劣势。企业对其的使用尚还处在观望阶段,因此他缺乏大量的实践验证与经验总结。没有成功的大中型商业应用来证明其优越性与使用价值,绝大多数的企业并不愿意承担使用这种新技术,而造成的各种风险,因此其发展也受到了制约。
期待有更多的个人对新技术拥有持久的热情,建立完整的理论体系,以将这些技术投入到实际的商业使用中,而不是停留在新鲜但不实用的阶段,从而真正减轻开发人员的压力,提升开发效率。
六、参考文献
[1]Contoso 大学 - 1 - 为 ASP.NET MVC 应用程序创建 EF 数据模型
http://www.cnblogs.com/haogj/archive/2012/04/02/2430494.html
[2]When is Code First not code first?
http://blogs.msdn.com/b/adonet/archive/2011/03/07/when-is-code-first-not-code-first.aspx
[3]Code First to a New Database
http://msdn.microsoft.com/zh-cn/data/jj193542
[4]SQL Server Express
http://zh.wikipedia.org/wiki/SQL_Server_Express
[5]SQL Server 2012 Express LocalDB
http://msdn.microsoft.com/en-us/library/hh510202(v=SQL.110).aspx
[6] KeyAttribute类
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.keyattribute(v=vs.110)
[7] StringLengthAttribute类
[8] MaxLengthAttribute类
[9] ConcurrencyCheckAttribute类
[10] RequiredAttribute类
[11] TimestampAttribute类
[12] ComplexTypeAttribute类
[13] ColumnAttribute类
[14] TableAttribute类
[15] InversePropertyAttribute类
[16] ForeignKeyAttribute类
[17] DatabaseGeneratedAttribute类
[18] NotMappedAttribute类