本视频和分步演练介绍了面向新数据库的 Code First 开发。 此方案包括针对尚不存在、需要由 Code First 创建的数据库,或现有的空数据库,Code First 将向其添加新表。 Code First 允许使用 C# 或 VB.Net 类定义模型。 可以通过在类和属性上使用属性,或通过使用 Fluent API 来执行其他配置。
观看视频
此视频介绍面向新数据库的 Code First 开发。 此方案包括面向一个不存在的数据库,Code First 将创建该数据库,或者面向一个 Code First 将添加新表的空数据库。 Code First 允许使用 C# 或 VB.Net 类定义模型。 可以选择通过在类和属性上使用特性或通过使用Fluent API来执行附加配置。
作者: 罗文·米勒
先决条件
至少需要安装 Visual Studio 2010 或 Visual Studio 2012 才能完成本演练。
如果使用 Visual Studio 2010,则还需要安装 NuGet 。
1.创建应用程序
为了简单起见,我们将构建一个使用 Code First 执行数据访问的基本控制台应用程序。
- 打开 Visual Studio
- 文件 -> 新建 -> 项目...
- 从左侧菜单和控制台应用程序选择 Windows
- 输入 CodeFirstNewDatabaseSample 作为名称
- 选择 “确定”
2. 创建模型
让我们使用类定义一个非常简单的模型。 我们只是在Program.cs文件中定义它们,但在实际应用程序中,你将将类拆分为单独的文件,并可能将单独的项目拆分为单独的文件。
在 program 类定义下,Program.cs添加以下两个类。
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
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; }
}
你会注意到,我们正在将两个导航属性(Blog.Posts 和 Post.Blog)设为虚拟的。 这将启用实体框架的延迟加载功能。 延迟加载意味着当你尝试访问这些属性时,这些属性的内容将自动从数据库加载。
3. 创建上下文
现在,是时候定义派生上下文,它表示与数据库的会话,使我们能够查询和保存数据。 我们定义一个从 System.Data.Entity.DbContext 派生的上下文,并为模型中的每个类公开一个类型化的 DbSet
我们现在开始使用 Entity Framework 中的类型,因此我们需要添加 EntityFramework NuGet 包。
- 项目 -> 管理 NuGet 包... 注意:如果没有 “管理 NuGet 包...” 选项,则应安装 最新版本的 NuGet
- 选择“ 联机 ”选项卡
- 选择 EntityFramework 包
- 单击“安装”
在 Program.cs 顶部添加 System.Data.Entity 的 using 语句。
using System.Data.Entity;
在 Program.cs Post 类下方添加以下派生上下文。
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
下面是Program.cs现在应包含的内容的完整列表。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
namespace CodeFirstNewDatabaseSample
{
class Program
{
static void Main(string[] args)
{
}
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
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; }
}
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
}
这就是我们需要开始存储和检索数据的所有代码。 显然,幕后发生了相当多的事情,我们将马上查看,但首先让我们看一下实际效果。
4. 读取和写入数据
在Program.cs中实现 Main 方法,如下所示。 此代码创建上下文的新实例,然后使用它插入新的博客。 然后,它使用 LINQ 查询从按 Title 按字母顺序排序的数据库检索所有博客。
class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
Console.Write("Enter a name for a new Blog: ");
var name = Console.ReadLine();
var blog = new Blog { Name = name };
db.Blogs.Add(blog);
db.SaveChanges();
// Display all Blogs from the database
var query = from b in db.Blogs
orderby b.Name
select b;
Console.WriteLine("All blogs in the database:");
foreach (var item in query)
{
Console.WriteLine(item.Name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
现在可以运行应用程序并对其进行测试。
Enter a name for a new Blog: ADO.NET Blog
All blogs in the database:
ADO.NET Blog
Press any key to exit...
我的数据在哪里?
按照惯例,DbContext 已为你创建了一个数据库。
- 如果本地 SQL Express 实例可用(默认情况下随 Visual Studio 2010 一起安装),则 Code First 已在该实例上创建了数据库
- 如果 SQL Express 不可用,则 Code First 将尝试使用 LocalDB (默认情况下随 Visual Studio 2012 一起安装)
- 数据库以派生上下文的完全限定名称命名,在本例中为 CodeFirstNewDatabaseSample.BloggingContext
这些只是默认约定,可通过多种方式更改 Code First 使用的数据库,有关 DbContext 如何发现模型和数据库连接 主题中提供了更多信息。 可以在 Visual Studio 中使用服务器资源管理器连接到此数据库
视图 -> 服务器资源管理器
右键单击 “数据连接 ”,然后选择“ 添加连接...”
如果尚未从服务器资源管理器连接到数据库,则需要选择Microsoft SQL Server 作为数据源
连接到已安装的 LocalDB 或 SQL Express 版本。
现在可以检查 Code First 创建的架构。
DbContext 通过查看定义的 DbSet 属性来找出要包含在模型中的类。 然后,它使用默认的 Code First 约定集来确定表和列名称、确定数据类型、查找主键等。 在本演练的后面部分,我们将介绍如何重写这些约定。
5. 处理模型更改
现在是时候对模型进行一些更改了,当我们进行这些更改时,还需要更新数据库架构。 为此,我们将使用一个名为 Code First Migrations 的功能,简称迁移。
通过迁移,我们可以通过一组有序的步骤来描述如何升级(和降级)数据库架构。 每个步骤(称为迁移)都包含一些代码,这些代码描述要应用的更改。
第一步是为 BloggingContext 启用 Code First 迁移。
工具 -> 库包管理器 -> 包管理器控制台
在包管理器控制台中运行 Enable-Migrations 命令
新的迁移文件夹已添加到项目,其中包含两个项目:
- Configuration.cs – 此文件包含迁移将用于迁移 BloggingContext 的设置。 我们不需要更改本演练的任何内容,但您可以在此处指定种子数据、为其他数据库注册提供程序、更改生成迁移所用的命名空间等。
- <timestamp>_InitialCreate.cs – 这是您第一次进行的迁移,它表示已应用到数据库的变化,使其从空数据库变成一个包含博客和帖子表的数据库。 虽然我们让 Code First 自动为我们创建了这些表,但如今我们选择启用了迁移功能,这些表因此被转换为一个迁移。 Code First 还在本地数据库中记录了该迁移已被应用。 文件名上的时间戳用于排序目的。
现在,让我们对模型进行更改,将 URL 属性添加到 Blog 类:
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; }
}
- 在包管理器控制台中运行 Add-Migration AddUrl 命令。 Add-Migration 命令会检查自上次迁移以来的更改,并使用找到的任何更改搭建新迁移的基架。 我们可以为迁移命名;在本例中,我们将调用迁移“AddUrl”。 我们需要在 dbo.Blogs 表中添加一个可以保存字符串数据的 Url 列,这一点是从基架代码中得出的。 如果需要,我们可以编辑基架代码,但在这种情况下不需要。
namespace CodeFirstNewDatabaseSample.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");
}
}
}
- 在包管理器控制台中运行 Update-Database 命令。 此命令会将任何未完成的迁移应用于数据库。 已经应用了我们的 InitialCreate 迁移,所以现在迁移操作将仅限于新的 AddUrl 迁移。 提示:调用 Update-Database 时,可以使用 –Verbose 开关来查看针对数据库执行的 SQL。
现在,新的 URL 列将添加到数据库中的 Blogs 表:
6. 数据批注
到目前为止,我们只是让 EF 使用其默认约定来发现模型,但有时我们的类不遵循约定,我们需要能够执行进一步的配置。 这有两个选项:我们将在本部分中查看数据注释,然后在下一部分中查看 Fluent API。
- 让我们将 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; }
}
- 如果尝试添加迁移,则会收到一条错误,提示“EntityType 'User' 未定义主键。请为此 EntityType 定义主键。”,因为 EF 无法知道用户名应该是 User 的主键。
- 现在我们将使用数据注释,因此我们需要在Program.cs顶部添加 using 语句
using System.ComponentModel.DataAnnotations;
- 现在批注 Username 属性以标识它是主键
public class User
{
[Key]
public string Username { get; set; }
public string DisplayName { get; set; }
}
- 使用 Add-Migration AddUser 命令搭建迁移基架,将这些更改应用于数据库
- 运行 Update-Database 命令,将新迁移应用到数据库
新表已添加到数据库中:
EF 支持的批注的完整列表为:
- KeyAttribute
- StringLengthAttribute
- MaxLengthAttribute
- ConcurrencyCheckAttribute
- RequiredAttribute
- TimestampAttribute
- ComplexTypeAttribute
- ColumnAttribute
- TableAttribute
- InversePropertyAttribute
- ForeignKeyAttribute
- DatabaseGeneratedAttribute
- NotMappedAttribute
7. Fluent API
在上一部分中,我们讨论了使用数据注释来补充或覆盖通过约定检测到的内容。 配置模型的另一种方法是通过 Code First fluent API。
大多数模型配置都可以使用简单的数据注释来完成。 Fluent API 是指定模型配置的更高级方法,它不仅涵盖了数据注释能够实现的所有功能,并且还提供一些数据注释无法实现的更高级配置。 数据注释和 Fluent API 可以一起使用。
若要访问 Fluent API,需要在 DbContext 中重写 OnModelCreating 方法。 假设我们想将存储 User.DisplayName 的列重命名为 display_name。
- 使用以下代码重写 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");
}
}
- 使用 Add-Migration ChangeDisplayName 命令搭建迁移基架,将这些更改应用于数据库。
- 运行 Update-Database 命令,将新的迁移应用到数据库。
DisplayName 列现在重命名为display_name:
总结
在本演练中,我们了解了使用新数据库进行 Code First 开发。 我们使用类定义模型,然后使用该模型创建数据库并存储和检索数据。 创建数据库后,我们使用 Code First 迁移来更改模型演变时的架构。 我们还了解了如何使用数据注释和 Fluent API 配置模型。