EF4.3于2月9号正式发布了,微软的EF小组最近一年开始发力,在4.3版本中新加了重要的特性:“数据库迁徙”。对于ORM框架来说,没有Code First功能,几乎没有吸引力,因为很少开发人员会去用那些自动生成的、难以驾驭的实体类,不够优雅。在Python等语言中的ORM框架,早就有了Code First的功能,而对于EF之前都是Databas First或者Model First,Code First在EF4中才出来,而且很不完善,其中最大的不足就是数据库模型的维护比较麻烦,无法实现添加或者删除实体的某些字段后,方便的更新数据库中的表结构,需要开发人员花大量精力去维护数据库结构的升级以及保留历史数据,在开发过程中很不方便。 在这次的4.3版本中,数据库迁徙功能终于是千呼万唤始出来。微软的产品有个特点,早期很难用,但是越到后面越好用,慢慢的超过同类产品,所以对于.NET开发人员,我个人觉得是时候下功夫去深入学习并在实际项目中应用EF了,我和我的团队已经开始了。
阅读目录
1. 数据库迁徙: 这是EF4.3中最重要的新特性,这样我们就能将精力集中在程序员喜欢做的事情上:写简单传统的CLR实体(Plain Old CLR Object),而不是写SQL语句去维护数据库,想添加一个字段就添加,想删除就删除,想修改名字就修改,是不是很爽?
2. 删除了EdmMetadata表:这个表就是一个半成品,用CodeFirst根本不会用这个表去维护数据库,因为有了数据库迁徙功能,数据库的结构信息能够自动生成,因此这个表就是多余的了。
3. 非Public字段上也可以添加数据描述属性了:以前默认情况下,Code First 会忽略private、protected和internal字段上的属性信息,即使你写代码将这些字段包含到你的模型里面(通过Fluent API),这些字段上的属性信息也会被忽略。在这个版本中,如果你通过Fluent API将这些非Public字段加入你的模型,所有属性信息都能起作用了。
4. 能够在配置文件中进行更多的设置: 在App/Web.config 文件中能针对Code First方式进行更多的配置,比如设置默认的数据库的Connection Factory和数据库初始化器,甚至可以在配置文件中设置实体对象的构造函数参数。更多的配置信息可以在官方博客中找到。
5. 修改了一系列的Bug,包括Unicode的DbSet名称等等。
下载下面示例代码
名称 | 说明 |
VS2010 | 因为要用NuGet |
NuGet | 数据库迁徙用到了NuGet的命令行工具,NuGet是非常有用的工具,如果还没有用过这个工具,可以百度一下,也可以去开源社区了解和学习,最好升级到最新版本(我是1.6)。 |
EF4.3 | 这个可以直接用NuGet,安装并引用到你的项目中。如果你安装过beta版本,相信你也会用NuGet的命令工具升级: Update-Package EntityFramework |
软件开发是一个迭代增量的过程,没有人能够一开始就完全设计好数据模型,因此对于Code First来说,最常见的开发场景是随着功能需求不断完善我们的数据模型。下面开始一步一步尝试数据库迁徙功能。在示例中我用Asp.net MVC3项目,首先新建一个项目,选择空模板,默认情况会引用一个EntityFramework,但很可能是4.1版本,删除这个引用,我们用NuGet将4.3版本的EF引入MVC3中:
当然你也可以通过命令行的方式(打开NuGet的Package Management Console,PMC):PM> Install-package EntityFramework
假设我们要建立的是一个网上书店,首先我们通过Code First建立我们的模型:
public class BookContext : DbContext
{
public DbSet<Book> Books { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public double Price { get; set; }
}
非常简单的模型,下一步是在我们网站上使用这个模型,将书以列表的方式显示出来,第一步添加HomeController:
public class HomeController : Controller
{
public ActionResult Index()
{
BookContext db = new BookContext();
db.Books.Add(new Book { Title = "Think in C++", Price = 59 });
db.SaveChanges();
return View(db.Books.ToList());
}
}
第二步添加对应的View:
@model List<MigarationDemo.Models.Book>
@{
ViewBag.Title = "网上书店";
}
<h2>书店里面有以下书籍</h2>
<ul>
@{
foreach (var item in Model) {
<li>书名: @item.Title</li>
}
}
</ul>
然后F5,你就应该在浏览器里面看到下面的结果:
我们什么也没干,一个简单的网站就完成了,是不是很神奇?我们甚至没有配置数据库连接字符串。这都是因为EF设计的原则是“约定好于配置” (Convention Over Configuration),默认情况下,EF找你本机上的SqlExpress数据库,因为我机器上有这个数据库,所以EF就能自动连接数据库,然后根据上面的数据模型生成对应的数据库:
如果到这里,一切都正常,让我们进入正题,使用EF4.3中的Migaration功能:
第一步:修改我们的模型,由于新的需求,每本书还应该有一个ISBN编号:
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public double Price { get; set; }
public string ISBN { get; set; }
}
如果这时候,你按F5,运行系统,会出现非法操作异常:“The model backing the 'BookContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).” 提示我们,应该进行数据库迁移。
很简单,只需要在PMC中执行下面3个命令:
1. Enable-Migrations
2. Add-Migration Book-ISBN (Book-ISBN是任意一个名称标记,用来标记一个迁移点)
3. Update-Database
如果执行完上面三个命令,并且一切顺利,恭喜你,数据库升级完成,你应该在项目里面看到:
并且数据库已经变成:
这时候F5,运行系统,应该一切正常了,你可以尝试在Controller中加入另外一条记录,我加入了:
db.Books.Add(new Book {
Title = "Think in C++ Volumne Two: Practical Programming",
Price = 59,
ISBN = "978-7-111-17115-7"
});
因此看到的是:
到此为止,我们已经一步一步完成了最简单的数据库迁徙/升级, 在上面生成的Migarations文件夹中主要一个配置文件和一个数据库迁移文件,数据库迁移文件有两个函数,一个是Up,即升级数据库,一个事Down,即回溯数据库,去看看生成的代码很容易理解它干了什么。
虽然EF能够自动根据数据模型生成对应的数据库结构,但是通常自动生成的数据库只是最基本的,通常我们需要设置更多的属性。比如上面的Book数据模型,默认的数据库是以BookId这个主键建立索引,可是我们还希望以Title建立索引,这样我们能够快速的根据书名查找。另外,上面示例中,添加ISBN字段后,已经有了的数据默认是Null,这会在程序中引起不必要的麻烦,我们假设希望已有的记录的ISBN是000-0-000-00000-0。
面对上面这些需求,我们首先通过下面命令新建一个迁徙:
Add-Migration IndexTitleAndDefault
在项目树的Migrations文件夹下面会生成一个新的cs文件,我的是201202191349250_IndexTitleAndDefault.cs,注意前面时间戳会不一样。
然后修改生成的迁移文件:
public partial class IndexTitleAndDefault : DbMigration
{
public override void Up()
{
AlterColumn("Books", "Title", c => c.String(nullable: false,maxLength:100));
CreateIndex("Books", "Title", true,"Index_Title");
AlterColumn("Books", "ISBN", c => c.String(nullable: false, defaultValue: "000-0-000-00000-0"));
}
public override void Down()
{
DropIndex("Books", "Index_Title");
}
}
最后执行命令:Update-Database,数据库应该建立了Title的索引,并且ISBN的默认值是000-0-000-00000-0。
上面所有操作都是更新到最新的版本,但有时候我们希望数据库能回溯到以前的版本,这在以前手动维护数据库时候是比较困难的,而EF4.3提供的数据库变迁特性能很方便的进行回溯:
只需要执行Updat-Database命令的时候,加入–TargetMigration:"XXX" 参数即可,比如我们想回到第二步的版本,我们只需要执行:
PM> Update-Database –TargetMigration:"201202190625099_Book-ISBN"
但是为了实现版本回溯,我们就得特别注意实现Down函数,相当于Down函数式Up函数的反函数。
另外执行Update-Database命令的时候加入-Script参数,这样就不是直接修改数据库结构,而是生成相应的Sql脚本:
Update-Database -Script -SourceMigration:"aaa" -TargetMigration:"bbb"
如果不指定TargetMigration,就默认是迁徙到最新的版本。
据微软的开发团队透露,EF的下一个版本是EF5,微软的版本号非常混乱,最开始EF是1.0,后来说要和.Net Framwork的版本号统一,所以直接就上到了EF4,现在.Net Framework的下一个版本是4.5,可是EF又要变成5了,他们在搞什么? 不管怎么样,看这个势头,EF5还是很值得期待的,有可能有以下改进:
1. 支持枚举类型
2. 性能将有很大提升
3. 改进SQL语句的生成,目前EF生成的Sql语句会比较复杂,在下一版本中将可能改善。
如果你希望EF有什么功能,可以去他们的网站投票,虽然最后决定权在微软Guy,但是你的一票可能会影响他们选择先开发什么功能。
现在看来,EF正在成为一个优秀的ORM框架,将能极大的提高程序员的开发效率,能够将程序员从数据访问层解脱出来,专注于业务逻辑层。当然,要掌握好EF,需要更加深入的学习,比如应该了解它都有哪些约定,应该了解一下它为我们生成的Sql语句,这样有助于我们写出高效的代码。