文献参考
MSDN EF框架官方文档
此文章完全根据微软官方文档资料,整个过程我已经在visual studio 中走了一遍,在这里将其总结下来,为以后复习所用,也为需要入门 Entity Framework 的朋友作为一个参考资料。力求准确无误。
创建模型(Model)
public class Blog
{
public int BlogId { get; set; } //这是EF默认的主键名称,ClassName+id,以便EF框架自动辨识
public string Name { get; set; }
public virtual List<Post> Posts { get; set; } //这是一个导航属性,注意virtual的用法
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; } //这个是外键,BlogId这个名称是标准的命名方法,需要注意
public virtual Blog Blog { get; set; } //这是一个导航属性,注意virtual的用法
}
说明:
1. 导航属性分别为
Blog.Posts and Post.Blog
。
2. 通过使导航属性
virtual
化,确保了EF框架的延迟加载特征( Lazy Loading
feature),延迟加载意味着当访问它们时,这些属性的内容将会被自动地从数据库中加载出来。
创建上下文(context)
现在是时候定义一个派生的上下文了,它代表着同数据库(database)的一次会话(session),允许我们去查询和保存数据。我们定义一个上下文,它派生于
System.Data.Entity.DbContext
,暴露出一个泛型成员
DbSet<TEntity>
。因为要用到EF框架的类型,所以我们需要添加
EntityFramework NuGet package
,添加的方法见地址: https://msdn.microsoft.com/en-us/library/jj193542(v=vs.113).aspx,第3节。
添加命名空间
using System.Data.Entity;
,定义下面的派生类:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
完整的代码如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity; //DbContext派生类引用
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; }
}
}
读写数据
实现Main 方法。这个代码创建了一个我们定义的上下文的一个实例,然后用它插入一条 Blog 实例。然后用 LINQ 查询,从数据库中追溯所有的Blog实例,并按照题目的字母顺序排序。
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();
}
}
}
在运行程序前,需要配置App.config文件中的数据库链接,我自己运行时,是现在数据库中创建一个EFDemoDb数据库,否则运行程序时,在DbSet.Add处总是提示没有在master数据库创建新数据库权限。这样按照要求配置好数据库的链接后,运行完毕后,在数据库中增加了两张表。注意观察。
<connectionStrings>
<add name="BloggingContext"
connectionString="Data Source=(local); Database=EFDemoDb;
User ID=sa; Password=sa;" providerName="System.Data.SqlClient"/>
</connectionStrings>
处理数据模型的变化
改到我们对我们的模型做一些变化的时候了,当我们在程序Model中变化时,我们也需要更新数据库的架构(schema)。为了做这些,我们要用 Code First Migrations,简称 Migrations。
Migrations 允许我们经过若干步骤来升级(upgrade)或还原(downgrade)我们的数据库架构。这些步骤中的每一步,称作 一次迁移(Migration),包含一些代码来描述将要应用的变化。
这个工具的位置在 工具->Package Manager Console。运行它,然后输入 Enable-Migrations,此时会自动增加一个 Migrations的文件夹,包含了2项,
Configuration.cs – This file contains the settings that Migrations will use for migrating BloggingContext. We don’t need to change anything for this walkthrough, but here is where you can specify seed data, register providers for other databases, changes the namespace that migrations are generated in etc.
*_InitialCreate.cs – This is your first migration, it represents the changes that have already been applied to the database to take it from being an empty database to one that includes the Blogs and Posts tables. Although we let Code First automatically create these tables for us, now that we have opted in to Migrations they have been converted into a Migration. Code First has also recorded in our local database that this Migration has already been applied. The timestamp on the filename is used for ordering purposes.
现在我们对模型做一个改变,为Blog模型增加一个 Url 属性。在 Package Manager Console中运行 Add-Migration AddUrl 命令,在Migrations文件夹下自动增加 201705140211306_AddUrl.cs文件,然后补充如下所示:
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 命令。这个命令将应用任何即将的迁移到数据库中。
Tip: You can use the –Verbose switch when calling Update-Database to see the SQL that is being executed against the database.
数据注释(Data Annotations)
至今我们已经让EF发现了用它的默认值去探索了模型,但是有时候我们的类并没有遵守这些默认值,我们需要进一步的配置。有两种方法可以做到:我们看一看数据注释(Data Annotations)和 fluent API。
让我们增加一个User模型,
public class User
{
public string Username { get; set; }
public string DisplayName { get; set; }
}
在DBContext中增加,
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; } //users
}
添加
using System.ComponentModel.DataAnnotations;
引用,通过Data Annotations 对 User添加主键:
public class User
{
[Key]
public string Username { get; set; }
public string DisplayName { get; set; }
}
完整的Data Annotations包括:(详细见 https://msdn.microsoft.com/en-us/library/jj193542(v=vs.113).aspx 第6部分)
KeyAttribute
StringLengthAttribute
MaxLengthAttribute
ConcurrencyCheckAttribute
RequiredAttribute
TimestampAttribute
ComplexTypeAttribute
ColumnAttribute
TableAttribute
InversePropertyAttribute
ForeignKeyAttribute
DatabaseGeneratedAttribute
NotMappedAttribute
Fluent API
The fluent API is a more advanced way of specifying model configuration that covers everything that data annotations can do in addition to some more advanced configuration not possible with data annotations. Data annotations and the fluent API can be used together.
覆写 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 命令迁移到数据库。