尖端编程,全自动员搬迁移数据库的完结

在付出涉及到数据库的程序时,常会遇上一开端筹划的构造无法满足供给供给再添加新字段或新表的处境,那时就要求展开数据库迁移。
实现数据库迁移有很多样艺术,从手动管理种种版本的ddl脚本,到得以实现协调的migrator,或是使用Entity
Framework提供的Code First迁移功效。
Entity
Framework提供的搬迁功效能够满足超越二分之一人的急需,但仍会设有难以分档次管理迁移代码和易于并发”context
has changed”错误的标题。

在开发涉及到数据库的次序时,常会碰到壹起先设计的布局不能够知足急需必要再添加新字段或新表的情景,那时就须要进行数据库迁移。
贯彻数据库迁移有很八种主意,从手动管理各样版本的ddl脚本,到达成本身的migrator,或是使用Entity
Framework提供的Code First迁移效用。
Entity
Framework提供的迁移功用能够知足抢先四分之几位的必要,但仍会设有难以分门类管理迁移代码和易于并发”context
has changed”错误的难点。

**译文,个人原创,转发请注脚出处(C# 6 与 .NET Core 1.0 高级编制程序 – 38 章 实体框架宗旨(上)),不对的地点欢迎建议与调换。** 

章节出自《Professional
C# 六 and .NET Core
1.0》。水平有限,各位阅读时仔细鉴定区别,唯望莫误人子弟。

附英文版原版的书文:Professional C# 6 and .NET Core 1.0 – 38 Entity
Framework
Core

本章节译文分为上下篇,下篇见: C# 6 与 .NET Core 1.0 高级编制程序 – 3八 章
实体框架大旨(下)


本章内容

  • Entity
    Framework Core 1.0简介
  • 使用依赖注入实体框架
  • 创立关系模型
  • 使用.NET
    CLI工具和MSBuild举办搬迁
  • 对象跟踪
  • 更新指标和对象树
  • 争论处理与更新
  • 运用工作

Wrox.Com关于本章的源代码下载

本章的wrox.com代码下载位于
www.wrox.com/go/professionalcsharp6下载代码选项卡。本章的代码主要有以下示例:

  • Books
    Sample
  • Books
    Sample with DI
  • Menus
    Sample
  • Menus
    with Data Annotations
  • Conflict
    Handling Sample
  • Transactions
    Sample 

**译文,村办原创,转发请注解出处(C# 陆 与 .NET Core 一.0 高级编制程序 – 38 章 实体框架宗旨(上)),不对的地点欢迎提出与调换。** 

章节出自《Professional
C# 六 and .NET Core
一.0》。水平有限,各位阅读时仔细辨认,唯望莫误人子弟。

附英文版原版的书文:Professional C# 6 and .NET Core 1.0 – 38 Entity
Framework
Core

本章节译文分为上下篇,下篇见: C# 6 与 .NET Core 壹.0 高级编制程序 – 3八 章
实体框架宗旨(下)


本章内容

  • Entity
    Framework Core 1.0简介
  • 动用注重注入实体框架
  • 创制关系模型
  • 使用.NET
    CLI工具和MSBuild举办搬迁
  • 对象跟踪
  • 更新目的和对象树
  • 争持处理与更新
  • 应用工作

Wrox.Com关于本章的源代码下载

本章的wrox.com代码下载位于
www.wrox.com/go/professionalcsharp6下载代码选项卡。本章的代码重要有以下示例:

  • Books
    Sample
  • Books
    Sample with DI
  • Menus
    Sample
  • Menus
    with Data Annotations
  • Conflict
    Handling Sample
  • Transactions
    Sample 

那边作者将介绍ZKWeb网页框架在Fluent
NHibernate和Entity Framework Core上利用的不贰秘籍。
能够完毕添加实体字段后,只需刷新网页就足以把改变应用到数据库。

此地作者将介绍ZKWeb网页框架在Fluent
NHibernate和Entity Framework Core上利用的法子。
能够做到添加实体字段后,只需刷新网页就足以把改变应用到数据库。

实体框架的历史

实业框架是提供实体到关系的照耀的框架。通过那种艺术,能够成立映射到多少库表的体系,使用LINQ成立数据库查询,创设和换代指标,并将它们写入数据库。 

因此长年累月对Entity
Framework的为数不多改动,最新的版本是3个完全的重写。1起来看望Entity
Framework的野史,以及重写的缘故。

  • Entity
    Framework 1
    —Entity Framework的第3个本子未有备选好与.NET
    3.⑤匹配,但它相当慢就能够与.NET 三.5 SP一包容。另一个出品LINQ to
    SQL提供了一些接近的功用,且已经可用于.NET 三.伍。 LINQ to SQL和Entity
    Framework在十分的大程度上提供了类似的服从。LINQ to
    SQL更易于使用,但不得不用来访问SQL
    Server。实体框架是基于提供程序的,并提供了对分化关周全据库的走访。它涵盖愈多的效应,例如多对多映射而不须要映射对象,n对n映射是唯恐的。
    Entity
    Framework的三个通病是它的模型类型供给由EntityObject基类派生。将目的映射到事关选取含有XML的EDMX文件实现的。包罗的XML由八个情势组成:概念格局定义(CSD)定义具有其天性和关联的靶子类型;存款和储蓄情势定义(SSD)定义数据库表、列和关系;以及映射方式语言(MSL)定义CSD和SSD怎样互相映射。

  • Entity
    Framework 4
    —Entity Framework ④ 在.NET
    4中万分,并且赢得了主要创新,当中许多出自LINQ到SQL的想法。由于变化较大,版本2和叁已被跳过。这几个本子里增添了延期加载以博得访问属性的涉及。在运用SQL数据定义语言(DDL)设计模型之后,能够创建数据库。以后应用Entity
    Framework的多个模型是Database First或Model
    First。大概最注重的特征是永葆简单对象类(POCO),由此不再需求从基类EntityObject派生。

趁着更新(例如Entity
Framework 4.一,四.二),NuGet包增添了额外的成效,由此能更快地丰硕效果。
Entity Framework 四.壹提供了Code
First模型,在那之中用于定义映射的EDMX文件不再选用。相反,所有的炫耀都使用C#代码定义

  • 采纳质量或Fluent API来定义的映照。

Entity Framework
四.三扩张了对搬迁的支撑。有了那或多或少,就能够运用C#代码定义数据库结构的改动。使用数据库从应用程序自动应用数据库更新。

  • Entity
    Framework 5
    —Entity Framework 5的NuGet包协助.NET 四.五和.NET
    四应用程序。然则,Entity Framework 伍的多多效应都可用于.NET 四.5。
    Entity Framework照旧基于.NET
    4.5在系统上安装的体系。此版本的新增效益是性质革新以及扶助新的SQL
    Server效能,例如空间数据类型。

  • Entity
    Framework 6
    —Entity Framework 六缓解了Entity Framework
    5的片段题材,其中壹些是设置在系统上的框架的1部分,1部分透过NuGet扩张提供。近期Entity
    Framework的整套代码已移至NuGet包。为了不造成争辨,使用了3个新的命名空间。将应用程序移植到新本龙时,必须变更命名空间。

本书探究Entity
Framework的风行版本,Entity Framework Core
①.0。此版本是一个去除旧的作为全面重写,不再扶助CSDL,SSDL和MSL的XML文件映射,只补助Code
First – 使用Entity Framework 4.1增加的模型。Code First
并不意味数据库无法先存在。您能够先成立数据库,大概仅从代码中定义数据库,以上三种选拔都以行得通的。

注意 Code First
这些称呼有个别程度上令人误解。Code First
先创造代码或先数据库都以卓有成效的。最初Code First的测试版本名称是Code
Only。因为其余模型选项在名称中有First,所以“Code
Only”的称呼也被改成。

Entity
Framework 的不可偏废重写不仅扶助关全面据库,还帮助NoSql数据库 –
只需求三个提供程序。在撰文本文时,提供程序支持少数,但相信会随时间而充实。 

新本子的Entity
Framework基于.NET
Core,由此在Linux和Mac系统上也足以动用此框架。 

Entity
Framework Core 一.0不完全扶助Entity Framework
⑥提供的拥有功能。随着时间的延迟,Entity
Framework的新本子将提供越多职能,留意所选取的Entity
Framework的版本。纵然选用Entity Framework 陆很多无敌的说辞,但在非Windows平台上行使ASP.NET Core 壹.0、Entity
Framework和通用Windows平台(UWP),以及非关周到据存款和储蓄,都急需使用Entity
Framework Core 1.0。 

本章介绍Entity
Framework Core 1.0。从多个简便的模子读取和SQL
Server中写入音讯起先,稍后会介绍添加关系,在写入数据库时将介绍更改跟踪器和冲突处理。利用搬迁成立和修改数据库结构是本章的另1个最重要片段。 

注意 本章使用Books数据库,此数据库蕴涵在演示代码的下载包中 www.wrox.com/go/professionalcsharp6. 

实业框架的野史

实体框架是提供实体到事关的映照的框架。通过那种方法,能够创造映射到数码库表的项目,使用LINQ创立数据库查询,创造和立异目的,并将它们写入数据库。 

透过多年对Entity
Framework的微量修改,最新的本子是1个截然的重写。一起来探望Entity
Framework的历史,以及重写的原由。

  • Entity
    Framework 1
    —Entity Framework的率先个版本未有防患于未然好与.NET
    三.伍男才女貌,但它高效就能够与.NET 三.5 SP一包容。另贰个出品LINQ to
    SQL提供了有个别近乎的法力,且已经可用于.NET 三.五。 LINQ to SQL和Entity
    Framework在相当大程度上提供了就像的机能。LINQ to
    SQL更易于使用,但只好用来访问SQL
    Server。实体框架是基于提供程序的,并提供了对区别关周详据库的拜会。它蕴涵越多的职能,例如多对多映射而不需求映射对象,n对n映射是唯恐的。
    Entity
    Framework的三个弱点是它的模型类型需求由EntityObject基类派生。将目的映射到事关选取带有XML的EDMX文件实现的。蕴含的XML由几个形式组成:概念方式定义(CSD)定义具有其属性和事关的对象类型;存款和储蓄方式定义(SSD)定义数据库表、列和关联;以及映射方式语言(MSL)定义CSD和SSD如何互相映射。

  • Entity
    Framework 4
    —Entity Framework 四 在.NET
    四中匹配,并且取得了主要创新,个中不少出自LINQ到SQL的想法。由于变化较大,版本二和三已被跳过。那一个本子里扩张了推迟加载以赢得访问属性的涉及。在行使SQL数据定义语言(DDL)设计模型之后,能够成立数据库。今后接纳Entity
    Framework的两个模型是Database First或Model
    First。大概最重要的特点是帮忙不难对象类(POCO),由此不再须要从基类EntityObject派生。

随着更新(例如Entity
Framework 4.一,4.二),NuGet包增添了额外的效能,由此能更快地抬高效果。
Entity Framework ④.1提供了Code
First模型,当中用于定义映射的EDMX文件不再利用。相反,全数的投射都利用C#代码定义

  • 应用质量或Fluent API来定义的照射。

Entity Framework
肆.三扩张了对搬迁的扶助。有了那一点,就足以采用C#代码定义数据库结构的更改。使用数据库从应用程序自动应用数据库更新。

  • Entity
    Framework 5
    —Entity Framework 伍的NuGet包帮忙.NET 4.伍和.NET
    肆应用程序。可是,Entity Framework 5的成都百货上千功力都可用于.NET 四.5。
    Entity Framework如故基于.NET
    四.5在系统上安装的类型。此版本的新增效益是性质立异以及支持新的SQL
    Server作用,例如空间数据类型。

  • Entity
    Framework 6
    —Entity Framework 陆消除了Entity Framework
    伍的有的题材,个中壹些是设置在系统上的框架的一部分,一部分通过NuGet扩展提供。方今Entity
    Framework的全部代码已移至NuGet包。为了不造成冲突,使用了3个新的命名空间。将应用程序移植到新本申时,必须改变命名空间。

本书研究Entity
Framework的新颖版本,Entity Framework Core
1.0。此版本是1个去除旧的表现周详重写,不再协助CSDL,SSDL和MSL的XML文件映射,只支持Code
First – 使用Entity Framework 四.1增加的模子。Code First
并不意味数据库不可能先存在。您能够先创建数据库,可能仅从代码中定义数据库,以上三种选取都以行得通的。

注意 Code First
那么些称谓有些程度上令人误解。Code First
先成立代码或先数据库都是卓有效用的。最初Code First的测试版本名称是Code
Only。因为任何模型选项在名称中有First,所以“Code
Only”的名目也被更改。

Entity
Framework 的通盘重写不仅协理关全面据库,还支持NoSql数据库 –
只要求三个提供程序。在写作本文时,提供程序帮衬有限,但相信会随时间而充实。 

新本子的Entity
Framework基于.NET
Core,由此在Linux和Mac系统上也足以动用此框架。 

Entity
Framework Core 1.0不完全帮忙Entity Framework
陆提供的有着机能。随着时光的延迟,Entity
Framework的新本子将提供越来越多职能,留意所利用的Entity
Framework的本子。固然选择Entity Framework 陆很多强有力的理由,但在非Windows平台上使用ASP.NET Core 壹.0、Entity
Framework和通用Windows平台(UWP),以及非关周全据存款和储蓄,都亟待运用Entity
Framework Core 一.0。 

本章介绍Entity
Framework Core 一.0。从3个简短的模型读取和SQL
Server中写入音讯开头,稍后会介绍添加关系,在写入数据库时将介绍更改跟踪器和争辨处理。利用搬迁创造和改动数据库结构是本章的另三个重点部分。 

注意 本章使用Books数据库,此数据库包蕴在示范代码的下载包中 www.wrox.com/go/professionalcsharp6. 

贯彻机关迁移的思路

数据库迁移须求钦命变更的1些,例如添加表和增加字段。
而落成活动员搬迁移供给自动生成那么些改变的局部,具体来说必要

  • 取得数据库现有的组织
  • 获得代码中幸存的结构
  • 相对而言结构之间的区别并转移迁移

这便是Entity Framework的Add-Migration(或dotnet ef migrations
add)命令所做的业务,
接下去我们将看什么不接纳那类的一声令下,在NHibernate, Entity
Framework和Entity Framework Core中完成全自动的处理。

贯彻机关迁移的思路

数据库迁移须求内定变更的部分,例如添加表和拉长字段。
而落到实处全自动员搬迁移要求自动生成这么些改变的片段,具体来说要求

  • 取得数据库现有的组织
  • 得到代码中幸存的结构
  • 相对而言结构之间的距离并生成迁移

那正是Entity Framework的Add-Migration(或dotnet ef migrations
add)命令所做的事体,
接下去大家将看哪样不采纳那类的指令,在NHibernate, Entity
Framework和Entity Framework Core中落成全自动的处理。

实业框架简介

首先个示范使用单个Book类型,并将此类型映射到SQL
Server数据库中的Books表。能够将记录写入数据库,然后读取,更新和删除它们。 

在首先个示范中,首先创立数据库。可以选取Visual
Studio 二零一五中的SQL
Server对象财富管理器执行此操作。采用数据库实例(与Visual
Studio一起安装的(localdb)\
MSSQLLocalDB),单击树视图中的数据库节点,然后采纳“添加新数据库”。示例数据库只有二个名字为Books的表。 

选择Books数据库中的表节点,然后选用”添加新表”来成立表Books。使用图38.第11中学所示的设计器,或然经过在T-SQL编辑器中输入SQL
DDL语句,都得以创造表Books。以下代码段呈现了用来创立表的T-SQL代码。单击“更新”按钮能够将改变提交到数据库。

CREATE TABLE [dbo].[Books]
(
  [BookId] INT NOT NULL PRIMARY KEY IDENTITY,
  [Title] NVARCHAR(50) NOT NULL,
  [Publisher] NVARCHAR(25) NOT NULL
)

实业框架简介

第2个示范使用单个Book类型,并将此类型映射到SQL
Server数据库中的Books表。能够将记录写入数据库,然后读取,更新和删除它们。 

在首先个示范中,首先创制数据库。能够行使Visual
Studio 2016中的SQL
Server对象财富管理器执行此操作。选用数据库实例(与Visual
Studio壹起安装的(localdb)\
MSSQLLocalDB),单击树视图中的数据库节点,然后选用“添加新数据库”。示例数据库唯有二个名叫Books的表。 

选拔Books数据库中的表节点,然后选拔”添加新表”来创制表Books。使用图3八.第11中学所示的设计器,或然通过在T-SQL编辑器中输入SQL
DDL语句,都足以创设表Books。以下代码段展现了用于创设表的T-SQL代码。单击“更新”按钮能够将转移提交到数据库。

CREATE TABLE [dbo].[Books]
(
  [BookId] INT NOT NULL PRIMARY KEY IDENTITY,
  [Title] NVARCHAR(50) NOT NULL,
  [Publisher] NVARCHAR(25) NOT NULL
)

Fluent NHibernate的机关迁移

ZKWeb框架使用的1体化代码能够翻看那里

首先Fluent
NHibernate供给添加全数实体的炫耀类型,以下是浮动配置和增进实业映射类型的例子。
配置类的构造得以尖端编程,全自动员搬迁移数据库的完结。翻开那里

var db = MsSqlConfiguration.MsSql2008.ConnectionString("连接字符串");
var configuration = Fluently.Configure();
configuration.Database(db);
configuration.Mappings(m => {
    m.FluentMappings.Add(typeof(FooEntityMap));
    m.FluentMappings.Add(typeof(BarEntityMap));
    ...
});

接下去是把具备实体的构造丰裕或更新到数据库。
NHibernate提供了SchemaUpdate,这些类能够自动物检疫查评定数据库中是否已经有表或字段,未有时自动抬高。
选择办法非凡简单,以下是运用的例证

configuration.ExposeConfiguration(c => {
    // 第一个参数 false: 不把语句输出到控制台
    // 第二个参数 true: 实际在数据库中执行语句
    new SchemaUpdate(c).Execute(false, true);
});

到这一步就早已落实了自动员搬迁移,但大家还有革新的后路。
因为SchemaUpdate不保留情形,每一遍都要检查评定数据库中的整个结构,所以进行起来EF的迁徙要磨磨蹭蹭很多,
ZKWeb框架为了减小每一遍运维网址的日子,在执行更新以前还会检验是或不是供给更新。

var scriptBuilder = new StringBuilder();
scriptBuilder.AppendLine("/* this file is for database migration checking, don't execute it */");
new SchemaExport(c).Create(s => scriptBuilder.AppendLine(s), false);
var script = scriptBuilder.ToString();
if (!File.Exists(ddlPath) || script != File.ReadAllText(ddlPath)) {
    new SchemaUpdate(c).Execute(false, true);
    onBuildFactorySuccess = () => File.WriteAllText(ddlPath, script);
}

这段代码应用了SchemaExport来变化全数表的DDL脚本,生成后和上次的变通结果相比较,不等同时才调用SchemaUpdate更新。

NHibernate提供的全自动员搬迁移有以下的性状,使用时应当专注

  • 字段只会加上,不会删除,假诺您重命名了字段原来的字段也会保留在数据库中
  • 字段类型假诺更改,数据库不会随之变动
  • 涉及的外键假使更改,迁移时有望会出错

总括NHibernate的自行迁移只会添加表和字段,基本不会修改原有的组织,有肯定的范围只是正如安全。

Fluent NHibernate的机动员搬迁移

ZKWeb框架使用的欧洲经济共同体代码能够查看那里

首先Fluent
NHibernate供给添加所有实体的映射类型,以下是转变配置和拉长实业映射类型的例子。
配置类的结构能够翻开那里

var db = MsSqlConfiguration.MsSql2008.ConnectionString("连接字符串");
var configuration = Fluently.Configure();
configuration.Database(db);
configuration.Mappings(m => {
    m.FluentMappings.Add(typeof(FooEntityMap));
    m.FluentMappings.Add(typeof(BarEntityMap));
    ...
});

接下去是把具备实体的构造丰富或更新到数据库。
NHibernate提供了SchemaUpdate,那几个类能够自动物检疫查测试数据库中是否已经有表或字段,未有时自动抬高。
接纳形式万分简单,以下是行使的事例

configuration.ExposeConfiguration(c => {
    // 第一个参数 false: 不把语句输出到控制台
    // 第二个参数 true: 实际在数据库中执行语句
    new SchemaUpdate(c).Execute(false, true);
});

到这一步就已经落到实处了机动员搬迁移,但大家还有创新的余地。
因为SchemaUpdate不保留景况,每回都要检查评定数据库中的整个结构,所以进行起来EF的迁徙要缓慢很多,
ZKWeb框架为了减少每一次运营网址的时日,在履行更新此前还会检查测试是还是不是须求立异。

var scriptBuilder = new StringBuilder();
scriptBuilder.AppendLine("/* this file is for database migration checking, don't execute it */");
new SchemaExport(c).Create(s => scriptBuilder.AppendLine(s), false);
var script = scriptBuilder.ToString();
if (!File.Exists(ddlPath) || script != File.ReadAllText(ddlPath)) {
    new SchemaUpdate(c).Execute(false, true);
    onBuildFactorySuccess = () => File.WriteAllText(ddlPath, script);
}

那段代码应用了SchemaExport来变化全体表的DDL脚本,生成后和上次的变更结果比较,不一样等时才调用SchemaUpdate更新。

NHibernate提供的自动员搬迁移有以下的特性,使用时应有专注

  • 字段只会增进,不会去除,假诺您重命名了字段原来的字段也会保留在数据库中
  • 字段类型假设更改,数据库不会跟着变动
  • 波及的外键假诺更改,迁移时有十分的大可能率会出错

小结NHibernate的自发性迁移只会添加表和字段,基本不会修改原有的结构,有早晚的界定只是正如安全。

始建立模型型 

用来访问Books数据库的演示应用程序BookSample是三个控制台应用程序(Package)。此示例使用以下正视项和命名空间:

  依赖项

NETStandard.Library
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer

   取名空间

Microsoft.EntityFrameworkCore
System.ComponentModel.DataAnnotations.Schema
System
System.Linq
System.Threading.Tasks
static System.Console

 澳门葡京备用网址 1


38.1
  

Book类是贰个简短的实体类型,它定义了多个属性。
BookId属性映射到表的主键,Title属性指向标题列,Publisher属性指向Publisher列。Table属性应用于类型将品种映射到Books表(代码文件BooksSample
/ Book.cs):

[Table("Books")]
public class Book
{
  public int BookId { get; set; }
  public string Title { get; set; }
  public string Publisher { get; set; }
}

创设模型 

用于访问Books数据库的演示应用程序Book萨姆ple是3个控制台应用程序(Package)。此示例使用以下依赖项和命名空间:

  依赖项

NETStandard.Library
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer

   取名空间

Microsoft.EntityFrameworkCore
System.ComponentModel.DataAnnotations.Schema
System
System.Linq
System.Threading.Tasks
static System.Console

 澳门葡京备用网址 2


38.1
  

Book类是一个简单易行的实业类型,它定义了多少个脾气。
BookId属性映射到表的主键,Title属性指向标题列,Publisher属性指向Publisher列。Table属性应用于类型将项目映射到Books表(代码文件BooksSample
/ Book.cs):

[Table("Books")]
public class Book
{
  public int BookId { get; set; }
  public string Title { get; set; }
  public string Publisher { get; set; }
}

Entity Framework的自行迁移

ZKWeb框架未有帮衬Entity Framework 陆,但落到实处比较简单笔者就直接上代码了。
例子

// 调用静态函数,放到程序启动时即可
// Database是System.Data.Entity.Database
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyConfiguration>());

public class MyConfiguration : DbMigrationsConfiguration<MyContext> {
    public MyConfiguration() {
        AutomaticMigrationsEnabled = true; // 启用自动迁移功能
        AutomaticMigrationDataLossAllowed = true; // 允许自动删字段,危险但是不加这个不能重命名字段
    }
}

Entity Framework提供的机关迁移有以下的表征,使用时应该小心

  • 只要字段重命名,旧的字段会被删除掉,推荐做好数据的备份和尽量制止重命名字段
  • 外键关联和字段类型都会自行生成,变化时有十分大希望会招致原有的数量丢失
  • 自行迁移的记录和使用工具迁移1样,都会保存在__MigrationHistory表中,切勿混用不然代码将不能够用到新的数据库中

小结Entity
Framework的动迁能够确定保障实体和数据库之间很强的1致性,但是使用不当会导致原有数据的丢失,请务必做好数据库的定时备份。

Entity Framework的自行迁移

ZKWeb框架未有帮衬Entity Framework 6,但贯彻相比较不难笔者就一贯上代码了。
例子

// 调用静态函数,放到程序启动时即可
// Database是System.Data.Entity.Database
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyConfiguration>());

public class MyConfiguration : DbMigrationsConfiguration<MyContext> {
    public MyConfiguration() {
        AutomaticMigrationsEnabled = true; // 启用自动迁移功能
        AutomaticMigrationDataLossAllowed = true; // 允许自动删字段,危险但是不加这个不能重命名字段
    }
}

Entity Framework提供的活动员搬迁移有以下的个性,使用时应该注意

  • 若果字段重命名,旧的字段会被剔除掉,推荐做好数据的备份和尽量幸免重命名字段
  • 外键关联和字段类型都会自行生成,变化时有希望会招致原有的多寡丢失
  • 电动员搬迁移的记录和应用工具迁移一样,都会保存在__MigrationHistory表中,切勿混用否则代码将无法用到新的数据库中

小结Entity
Framework的动员搬迁能够保障实体和数据库之间很强的壹致性,不过使用不当会招致原有数据的不见,请务必做好数据库的定时备份。

制造上下文 

成立的BooksContext类完结Book表与数据库的关联。那些类派生自基类DbContext,BooksContext类定义Books属性类型为DbSet
<Book>。此类型允许创造查询并添加Book实例以将其储存在数据库中。要定义连接字符串,能够重写DbContext的OnConfiguring方法。UseSqlServer扩充方法将左右文映射到SQL
Server数据库(代码文件Books萨姆ple / BooksContext.cs):

public class BooksContext: DbContext
{
  private const string ConnectionString =  @"server= (localdb)\MSSQLLocalDb;database=Books;trusted_connection=true";
  public DbSet<Book> Books { get; set; }
  protected override void OnConfiguring(DbContextOptionsBuilder  optionsBuilder)
  {
    base.OnConfiguring(optionsBuilder);
    optionsBuilder.UseSqlServer(ConnectionString);
  }
}

概念连接字符串的另1个抉择是选用正视注入,将在本章前边介绍。 

开创上下文 

始建的BooksContext类实现Book表与数据库的涉嫌。那一个类派生自基类DbContext,BooksContext类定义Books属性类型为DbSet
<Book>。此类型允许创设查询并添加Book实例以将其储存在数据库中。要定义连接字符串,能够重写DbContext的OnConfiguring方法。UseSqlServer扩大方法将左右文映射到SQL
Server数据库(代码文件Books萨姆ple / BooksContext.cs):

public class BooksContext: DbContext
{
  private const string ConnectionString =  @"server= (localdb)\MSSQLLocalDb;database=Books;trusted_connection=true";
  public DbSet<Book> Books { get; set; }
  protected override void OnConfiguring(DbContextOptionsBuilder  optionsBuilder)
  {
    base.OnConfiguring(optionsBuilder);
    optionsBuilder.UseSqlServer(ConnectionString);
  }
}

概念连接字符串的另二个取舍是运用重视注入,将在本章前面介绍。 

Entity Framework Core的自动员搬迁移

Entity Framework
Core去掉了SetInitializer慎选,取而代之的是DatabaseFacade.MigrateDatabaseFacade.EnsureCreated
DatabaseFacade.Migrate能够运用使用ef命令生成的迁徙代码,防止在生养环境中实施ef命令。
DatabaseFacade.EnsureCreated则始于创设全数数据表和字段,但只可以创造不能够更新,不会添加纪录到__MigrationHistory
那多少个函数都不能落实全自动员搬迁移,ZKWeb框架使用了EF内部提供的函数,完整代码能够翻看那里

Entity Framework Core的自行迁移完结相比复杂,我们必要分两步走。

  • 首先步
    创设迁移记录__ZKWeb_MigrationHistory表,那些表和EF自带的布局同样,但那些表是给协调用的不是给ef命令用的
  • 其次部
    查找最终一条迁移记录,和近年来的构造实行自己检查自纠,找出异样并更新数据库

首先步的代码应用了EnsureCreated创办数据库和迁移记录表,在那之中EFCoreDatabaseContextBase只有迁移记录贰个表。
创造完之后还要把带迁移记录的结构保留下来,用作前边的争执统1,假使那里不保留会促成迁移记录的再一次制造错误。

using (var context = new EFCoreDatabaseContextBase(Database, ConnectionString)) {
    // We may need create a new database and migration history table
    // It's done here
    context.Database.EnsureCreated();
    initialModel = context.Model;
}

在实践第一步事先,还亟需先判断连接的数据库是还是不是关全面据库,
因为Entity Framework Core今后还会支撑redis
mongodb等非关系型数据库,自动员搬迁移只应该用在关周密据库中。

using (var context = new EFCoreDatabaseContext(Database, ConnectionString)) {
    var serviceProvider = ((IInfrastructure<IServiceProvider>)context).Instance;
    var databaseCreator = serviceProvider.GetService<IDatabaseCreator>();
    if (databaseCreator is IRelationalDatabaseCreator) {
        // It's a relational database, create and apply the migration
        MigrateRelationalDatabase(context, initialModel);
    } else {
        // It maybe an in-memory database or no-sql database, do nothing
    }
}

其次步供给摸索最终一条迁移记录,和脚下的构造进行相比较,找出差距并更新数据库。

先看迁移记录表的剧情,迁移记录表中有八个字段

  • Revision 每回迁移都会+一
  • Model 当前的构造,格式是c#代码
  • ProductVersion 迁移时Entity Framework Core的版本号

Model存放的代码例子如下,那段代码记录了全部表的兼具字段的定义,是自动生成的。
末端小编将会讲课怎么着生成那段代码。

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using ZKWeb.ORM.EFCore;

namespace ZKWeb.ORM.EFCore.Migrations
{
    [DbContext(typeof(EFCoreDatabaseContext))]
    partial class Migration_636089159513819123 : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
            modelBuilder
                .HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

            modelBuilder.Entity("Example.Entities.Foo", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired();
                });
            }
        }
    }
}

接下去查找最后一条迁移记录:

var lastModel = initialModel;
var histories = context.Set<EFCoreMigrationHistory>();
var lastMigration = histories.OrderByDescending(h => h.Revision).FirstOrDefault();

存在时,编写翻译Model中的代码并且获得ModelSnapshot.Model的值,这么些值便是上一次迁移时的全部结构。
不存在时,将动用initialModel的结构。
编写翻译使用的是其它一个零部件,你也能够用Roslyn CSharpScripting包提供的接口编写翻译。

if (lastMigration != null) {
    // Remove old snapshot code and assembly
    var tempPath = Path.GetTempPath();
    foreach (var file in Directory.EnumerateFiles(
        tempPath, ModelSnapshotFilePrefix + "*").ToList()) {
        try { File.Delete(file); } catch { }
    }
    // Write snapshot code to temp directory and compile it to assembly
    var assemblyName = ModelSnapshotFilePrefix + DateTime.UtcNow.Ticks;
    var codePath = Path.Combine(tempPath, assemblyName + ".cs");
    var assemblyPath = Path.Combine(tempPath, assemblyName + ".dll");
    var compileService = Application.Ioc.Resolve<ICompilerService>();
    var assemblyLoader = Application.Ioc.Resolve<IAssemblyLoader>();
    File.WriteAllText(codePath, lastMigration.Model);
    compileService.Compile(new[] { codePath }, assemblyName, assemblyPath);
    // Load assembly and create the snapshot instance
    var assembly = assemblyLoader.LoadFile(assemblyPath);
    var snapshot = (ModelSnapshot)Activator.CreateInstance(
        assembly.GetTypes().First(t =>
        typeof(ModelSnapshot).GetTypeInfo().IsAssignableFrom(t)));
    lastModel = snapshot.Model;
}

和方今的构造进行相比较:

// Compare with the newest model
var modelDiffer = serviceProvider.GetService<IMigrationsModelDiffer>();
var sqlGenerator = serviceProvider.GetService<IMigrationsSqlGenerator>();
var commandExecutor = serviceProvider.GetService<IMigrationCommandExecutor>();
var operations = modelDiffer.GetDifferences(lastModel, context.Model);
if (operations.Count <= 0) {
    // There no difference
    return;
}

假使有出入,生成迁移命令(commands)和最近完整结构的快速照相(modelSnapshot)。
地方Model中的代码由那里的CSharpMigrationsGenerator生成,modelSnapshot的项目是string

// There some difference, we need perform the migration
var commands = sqlGenerator.Generate(operations, context.Model);
var connection = serviceProvider.GetService<IRelationalConnection>();
// Take a snapshot to the newest model
var codeHelper = new CSharpHelper();
var generator = new CSharpMigrationsGenerator(
    codeHelper,
    new CSharpMigrationOperationGenerator(codeHelper),
    new CSharpSnapshotGenerator(codeHelper));
var modelSnapshot = generator.GenerateSnapshot(
    ModelSnapshotNamespace, context.GetType(),
    ModelSnapshotClassPrefix + DateTime.UtcNow.Ticks, context.Model);

铺排迁移记录并履行迁移命令:

// Insert the history first, if migration failed, delete it
var history = new EFCoreMigrationHistory(modelSnapshot);
histories.Add(history);
context.SaveChanges();
try {
    // Execute migration commands
    commandExecutor.ExecuteNonQuery(commands, connection);
} catch {
    histories.Remove(history);
    context.SaveChanges();
    throw;
}

到此地就做到了Entity Framework
Core的自发性迁移,今后每一遍有更新都会比较最后三遍迁移时的构造并推行更新。
Entity Framework Core的迁移特点和Entity
Framework1样,能够确认保证很强的一致性但需求小心理防线护数据的不见。

Entity Framework Core的机动员搬迁移

Entity Framework
Core去掉了SetInitializer慎选,取而代之的是DatabaseFacade.MigrateDatabaseFacade.EnsureCreated
DatabaseFacade.Migrate能够行使使用ef命令生成的搬迁代码,制止在生养条件中进行ef命令。
DatabaseFacade.EnsureCreated则起初创立全部数据表和字段,但只可以创制不能够立异,不会添加纪录到__MigrationHistory
那四个函数都无法促成全自动员搬迁移,ZKWeb框架使用了EF内部提供的函数,完整代码能够翻开那里

Entity Framework Core的活动员搬迁移达成相比较复杂,大家需求分两步走。

  • 先是步
    成立迁移记录__ZKWeb_MigrationHistory表,那几个表和EF自带的组织同样,但以此表是给自个儿用的不是给ef命令用的
  • 第三部
    查找最终一条迁移记录,和日前的结构进行对照,找出异样并立异数据库

先是步的代码应用了EnsureCreated创办数据库和迁移记录表,在那之中EFCoreDatabaseContextBase唯有迁移记录二个表。
开创完现在还要把带迁移记录的结构保留下去,用作后边的对峙统壹,假如那里不保留会促成迁移记录的再一次创设错误。

using (var context = new EFCoreDatabaseContextBase(Database, ConnectionString)) {
    // We may need create a new database and migration history table
    // It's done here
    context.Database.EnsureCreated();
    initialModel = context.Model;
}

在执行第2步事先,还索要先判断连接的数据库是还是不是关全面据库,
因为Entity Framework Core现在还会匡助redis
mongodb等非关系型数据库,自动员搬迁移只应该用在关周详据库中。

using (var context = new EFCoreDatabaseContext(Database, ConnectionString)) {
    var serviceProvider = ((IInfrastructure<IServiceProvider>)context).Instance;
    var databaseCreator = serviceProvider.GetService<IDatabaseCreator>();
    if (databaseCreator is IRelationalDatabaseCreator) {
        // It's a relational database, create and apply the migration
        MigrateRelationalDatabase(context, initialModel);
    } else {
        // It maybe an in-memory database or no-sql database, do nothing
    }
}

第叁步需求寻找最后一条迁移记录,和当下的布局进行自己检查自纠,找出异样并更新数据库。

先看迁移记录表的始末,迁移记录表中有七个字段

  • Revision 每便迁移都会+一
  • Model 当前的结构,格式是c#代码
  • ProductVersion 迁移时Entity Framework Core的版本号

Model存放的代码例子如下,那段代码记录了全体表的装有字段的定义,是自动生成的。
背后笔者将会讲课如何生成那段代码。

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using ZKWeb.ORM.EFCore;

namespace ZKWeb.ORM.EFCore.Migrations
{
    [DbContext(typeof(EFCoreDatabaseContext))]
    partial class Migration_636089159513819123 : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
            modelBuilder
                .HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

            modelBuilder.Entity("Example.Entities.Foo", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired();
                });
            }
        }
    }
}

接下去查找最终一条迁移记录:

var lastModel = initialModel;
var histories = context.Set<EFCoreMigrationHistory>();
var lastMigration = histories.OrderByDescending(h => h.Revision).FirstOrDefault();

存在时,编写翻译Model中的代码并且赢得ModelSnapshot.Model的值,那个值便是上三遍迁移时的完好结构。
不存在时,将运用initialModel的结构。
编写翻译使用的是此外贰个零件,你也得以用Roslyn CSharpScripting包提供的接口编译。

if (lastMigration != null) {
    // Remove old snapshot code and assembly
    var tempPath = Path.GetTempPath();
    foreach (var file in Directory.EnumerateFiles(
        tempPath, ModelSnapshotFilePrefix + "*").ToList()) {
        try { File.Delete(file); } catch { }
    }
    // Write snapshot code to temp directory and compile it to assembly
    var assemblyName = ModelSnapshotFilePrefix + DateTime.UtcNow.Ticks;
    var codePath = Path.Combine(tempPath, assemblyName + ".cs");
    var assemblyPath = Path.Combine(tempPath, assemblyName + ".dll");
    var compileService = Application.Ioc.Resolve<ICompilerService>();
    var assemblyLoader = Application.Ioc.Resolve<IAssemblyLoader>();
    File.WriteAllText(codePath, lastMigration.Model);
    compileService.Compile(new[] { codePath }, assemblyName, assemblyPath);
    // Load assembly and create the snapshot instance
    var assembly = assemblyLoader.LoadFile(assemblyPath);
    var snapshot = (ModelSnapshot)Activator.CreateInstance(
        assembly.GetTypes().First(t =>
        typeof(ModelSnapshot).GetTypeInfo().IsAssignableFrom(t)));
    lastModel = snapshot.Model;
}

和当下的布局举行对照:

// Compare with the newest model
var modelDiffer = serviceProvider.GetService<IMigrationsModelDiffer>();
var sqlGenerator = serviceProvider.GetService<IMigrationsSqlGenerator>();
var commandExecutor = serviceProvider.GetService<IMigrationCommandExecutor>();
var operations = modelDiffer.GetDifferences(lastModel, context.Model);
if (operations.Count <= 0) {
    // There no difference
    return;
}

只要大有分裂,生成迁移命令(commands)和当前线总指挥部体结构的快速照相(modelSnapshot)。
地点Model中的代码由那里的CSharpMigrationsGenerator生成,modelSnapshot的品种是string

// There some difference, we need perform the migration
var commands = sqlGenerator.Generate(operations, context.Model);
var connection = serviceProvider.GetService<IRelationalConnection>();
// Take a snapshot to the newest model
var codeHelper = new CSharpHelper();
var generator = new CSharpMigrationsGenerator(
    codeHelper,
    new CSharpMigrationOperationGenerator(codeHelper),
    new CSharpSnapshotGenerator(codeHelper));
var modelSnapshot = generator.GenerateSnapshot(
    ModelSnapshotNamespace, context.GetType(),
    ModelSnapshotClassPrefix + DateTime.UtcNow.Ticks, context.Model);

安顿迁移记录并实施迁移命令:

// Insert the history first, if migration failed, delete it
var history = new EFCoreMigrationHistory(modelSnapshot);
histories.Add(history);
context.SaveChanges();
try {
    // Execute migration commands
    commandExecutor.ExecuteNonQuery(commands, connection);
} catch {
    histories.Remove(history);
    context.SaveChanges();
    throw;
}

到那里就水到渠成了Entity Framework
Core的自行迁移,以往每一回有更新都会比较最后贰遍迁移时的布局并实施更新。
Entity Framework Core的动员搬迁特点和Entity
Framework壹样,能够确定保证很强的一致性但要求注意防止数据的丢失。

写入数据库

今昔已开立了有Books表的数据库,也定义了模型和内外文类,然后能够用多少填充表。创造AddBookAsync方法将Book对象添加到数据库。首先,BooksContext对象被实例化,那里运用using语句确认保证数据库连接关闭。使用Add方法将对象添加到上下文之后,实体被写入调用SaveChangesAsync的数据库(代码文件BooksSample
/ Program.cs):

private async Task AddBookAsync(string title, string publisher)
{
  using (var context = new BooksContext())
  {
    var book = new Book
    {
      Title = title,
      Publisher = publisher
    };
    context.Add(book);
    int records = await context.SaveChangesAsync();

    WriteLine($"{records} record added");
  }
  WriteLine();
} 

要添加书籍列表,能够动用AddRange方法(代码文件BooksSample
/ Program.cs):

private async Task AddBooksAsync()
{
  using (var context = new BooksContext())
  {
    var b1 = new Book
    {
      Title ="Professional C# 5 and .NET 4.5.1",
      Publisher ="Wrox Press"
    };
    var b2 = new Book
    {
      Title ="Professional C# 2012 and .NET 4.5",
      Publisher ="Wrox Press"
    };
    var b3 = new Book
    {
      Title ="JavaScript for Kids",
      Publisher ="Wrox Press"
    };
    var b4 = new Book
    {
      Title ="Web Design with HTML and CSS",
      Publisher ="For Dummies"
    };
    context.AddRange(b1, b2, b3, b4);
    int records = await context.SaveChangesAsync();
    WriteLine($"{records} records added");
  }
  WriteLine();
} 

 运维应用程序并调用这个艺术后,能够采纳SQL
Server对象能源管理器查看写入到数据库的数量。

写入数据库

近日已创建了有Books表的数据库,也定义了模型和前后文类,然后能够用数码填充表。创立AddBookAsync方法将Book对象添加到数据库。首先,BooksContext对象被实例化,那里运用using语句确定保证数据库连接关闭。使用Add方法将目的添加到上下文之后,实体被写入调用SaveChangesAsync的数据库(代码文件Books萨姆ple
/ Program.cs):

private async Task AddBookAsync(string title, string publisher)
{
  using (var context = new BooksContext())
  {
    var book = new Book
    {
      Title = title,
      Publisher = publisher
    };
    context.Add(book);
    int records = await context.SaveChangesAsync();

    WriteLine($"{records} record added");
  }
  WriteLine();
} 

要添加书籍列表,能够运用AddRange方法(代码文件Books萨姆ple
/ Program.cs):

private async Task AddBooksAsync()
{
  using (var context = new BooksContext())
  {
    var b1 = new Book
    {
      Title ="Professional C# 5 and .NET 4.5.1",
      Publisher ="Wrox Press"
    };
    var b2 = new Book
    {
      Title ="Professional C# 2012 and .NET 4.5",
      Publisher ="Wrox Press"
    };
    var b3 = new Book
    {
      Title ="JavaScript for Kids",
      Publisher ="Wrox Press"
    };
    var b4 = new Book
    {
      Title ="Web Design with HTML and CSS",
      Publisher ="For Dummies"
    };
    context.AddRange(b1, b2, b3, b4);
    int records = await context.SaveChangesAsync();
    WriteLine($"{records} records added");
  }
  WriteLine();
} 

 运营应用程序并调用这个格局后,可以应用SQL
Server对象能源管理器查看写入到数据库的数目。

写在结尾

自动员搬迁移数据库假诺不易利用,能够加强项目中逐条模块的独立性,减弱支出和安排的工作量。
不过因为不能够手动控制搬迁内容,有肯定的受制和惊险,要求明白好利用的O汉兰达M迁移的特征。

写在最终

机动员搬迁移数据库尽管不易选择,可以增长项目中逐一模块的独立性,收缩支出和配备的工作量。
可是因为无法手动控制搬迁内容,有必然的局限和危急,须要精晓好使用的O昂科拉M迁移的性状。

从数据库读取

从C#代码读取数据只必要调用BooksContext并访问Books属性。访问此属性会创造2个SQL语句从数据库中检索全数图书(代码文件BooksSample
/ Program.cs):

private void ReadBooks()
{
  using (var context = new BooksContext())
  {
    var books = context.Books;
    foreach (var b in books)
    {
      WriteLine($"{b.Title} {b.Publisher}");
    }
  }
  WriteLine();
}

在调节和测试时期打开
英特尔liTrace 伊夫nts窗口,能够看来发送到数据库的SQL语句(须要Visual
Studio 公司版):

SELECT [b].[BookId], [b].[Publisher], [b].[Title]
FROM [Books] AS [b]

Framework提供了四个LINQ提供程序,能够创造LINQ查询访问数据库。能够行使如下所示语法的章程:

private void QueryBooks()
{
  using (var context = new BooksContext())
  {
    var wroxBooks = context.Books.Where(b => b.Publisher =="Wrox Press");
    foreach (var b in wroxBooks)
    {
      WriteLine($"{b.Title} {b.Publisher}");
    }
  }
  WriteLine();
}

或使用LINQ查询语法:

var wroxBooks = from b in context.Books
                where b.Publisher =="Wrox Press"
                select b;

利用那三种差别的语法,都将发送上面包车型客车SQL语句到数据库:

SELECT [b].[BookId], [b].[Publisher], [b].[Title]
FROM [Books] AS [b]
WHERE [b].[Publisher] = 'Wrox Press'

*注意 在第3三章“语言集成查询”中详尽座谈了LINQ。
*

从数据库读取

从C#代码读取数据只须要调用BooksContext并走访Books属性。访问此属性会创造三个SQL语句从数据库中查找全数图书(代码文件BooksSample
/ Program.cs):

private void ReadBooks()
{
  using (var context = new BooksContext())
  {
    var books = context.Books;
    foreach (var b in books)
    {
      WriteLine($"{b.Title} {b.Publisher}");
    }
  }
  WriteLine();
}

在调试时期打开
速龙liTrace 伊夫nts窗口,可以看到发送到数据库的SQL语句(需要Visual
Studio 集团版):

SELECT [b].[BookId], [b].[Publisher], [b].[Title]
FROM [Books] AS [b]

Framework提供了2个LINQ提供程序,能够创设LINQ查询访问数据库。能够应用如下所示语法的法子:

private void QueryBooks()
{
  using (var context = new BooksContext())
  {
    var wroxBooks = context.Books.Where(b => b.Publisher =="Wrox Press");
    foreach (var b in wroxBooks)
    {
      WriteLine($"{b.Title} {b.Publisher}");
    }
  }
  WriteLine();
}

或采纳LINQ查询语法:

var wroxBooks = from b in context.Books
                where b.Publisher =="Wrox Press"
                select b;

动用那三种分歧的语法,都将发送下边包车型地铁SQL语句到数据库:

SELECT [b].[BookId], [b].[Publisher], [b].[Title]
FROM [Books] AS [b]
WHERE [b].[Publisher] = 'Wrox Press'

*注意 在第三3章“语言集成查询”中详尽谈论了LINQ。
*

写在最后的广告

ZKWeb网页框架早已在事实上项目中应用了那项技艺,近年来来看迁移部分依然相比稳定的。
那项技术最初是为着插件商城而付出的,在下载安装插件未来不须要再行编写翻译主程序,不须求进行别的迁移命令就能运用。
近年来虽然并未有完毕插件商城,也收缩了很多平凡支出的干活。

万一您有趣味,欢迎到场ZKWeb沟通群5220838捌陆壹起探索。

写在终极的广告

ZKWeb网页框架早就在实际上项目中接纳了那项技术,方今来看迁移部分照旧比较稳定的。
那项技能最初是为了插件商城而支付的,在下载安装插件未来不要求再一次编译主程序,不要求执行别的迁移命令就能采用。
时下虽说并没有兑现插件商城,也缩减了不可胜计普通成本的工作。

设若您有趣味,欢迎出席ZKWeb调换群5220838八6同步探索。

更新记录

只需改变已加载上下文的对象并调用SaveChangesAsync即可轻松实现立异记录(代码文件Books萨姆ple
/ Program.cs):

private async Task UpdateBookAsync()
{
  using (var context = new BooksContext())
  {
    int records = 0;
    var book = context.Books.Where(b => b.Title =="Professional C# 6")
      .FirstOrDefault();
    if (book != null)
    {
      book.Title ="Professional C# 6 and .NET Core 5";
      records = await context.SaveChangesAsync();
    }
    WriteLine($"{records} record updated");
  }
  WriteLine();
}

立异记录

只需变更已加载上下文的目的并调用SaveChangesAsync即可轻松完毕立异记录(代码文件Books萨姆ple
/ Program.cs):

private async Task UpdateBookAsync()
{
  using (var context = new BooksContext())
  {
    int records = 0;
    var book = context.Books.Where(b => b.Title =="Professional C# 6")
      .FirstOrDefault();
    if (book != null)
    {
      book.Title ="Professional C# 6 and .NET Core 5";
      records = await context.SaveChangesAsync();
    }
    WriteLine($"{records} record updated");
  }
  WriteLine();
}

删去记录

末段,让我们清理数据库并剔除全数记录。能够通过寻找全体记录并调用Remove或RemoveRange方法来设置上下文中要刨除的指标的动静。然后调用SaveChangesAsync方法即可从数据库中去除记录,DbContext会为每一个要去除的对象调用SQL
Delete语句(代码文件Books萨姆ple / Program.cs):

private async Task DeleteBooksAsync()
{
  using (var context = new BooksContext())
  {
    var books = context.Books;
    context.Books.RemoveRange(books);
    int records = await context.SaveChangesAsync();
    WriteLine($"{records} records deleted");
  }
  WriteLine();
}

*注意 目的关系映射工具(如Entity
Framework)在毫不在富有方案中都可用。使用示例代码不能够有效地删除全数指标。您能够使用一个SQL语句删除全体而不是逐条删除记录。在第一7章“ADO.NET”中表明了什么样做到那或多或少。*

打探了何等添加、查询、更新和删除记录,本章将介绍幕后的功效,并运用Entity
Framework进入高级场景。

剔除记录

最终,让我们清理数据库并剔除全部记录。能够通过寻找全数记录并调用Remove或RemoveRange方法来设置上下文中要去除的指标的处境。然后调用SaveChangesAsync方法即可从数据库中删除记录,DbContext会为各样要去除的对象调用SQL
Delete语句(代码文件BooksSample / Program.cs):

private async Task DeleteBooksAsync()
{
  using (var context = new BooksContext())
  {
    var books = context.Books;
    context.Books.RemoveRange(books);
    int records = await context.SaveChangesAsync();
    WriteLine($"{records} records deleted");
  }
  WriteLine();
}

*注意 指标关系映射工具(如Entity
Framework)在并非在拥有方案中都可用。使用示例代码不能够有效地删除全部目的。您能够应用1个SQL语句删除全部而不是逐条删除笔录。在第2七章“ADO.NET”中解释了怎么着实现那一点。*

叩问了怎么样添加、查询、更新和删除记录,本章将介绍幕后的功用,并行使Entity
Framework进入高级场景。

使用信赖注入  

Entity
Framework Core 壹.0放到了对注重注入的补助。连接和SQL
Server选择能够经过行使依赖注入框架注入,而非定义和接下来使用DbContext派生类的SQL
Server连接。 

要查阅此操作,Books山姆pleWithDI示例项目对上多个代码示例项目开始展览了改动。 

此示例使用以下重视项和命名空间:

  依赖项

NETStandard.Library
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.Framework.DependencyInjection 

  命名空间

Microsoft.EntityFrameworkCore
System.Linq
System.Threading.Tasks
static System.Console

BooksContext类以后看起来一点也不细略,只需定义Books属性(代码文件Books萨姆pleWithDI
/ BooksContext.cs):

public class BooksContext: DbContext
{
  public DbSet<Book> Books { get; set; }
}

BooksService是运用BooksContext的新类。BooksContext通过注入构造函数注入。方法AddBooksAsync和ReadBooks与上2个演示中的那么些格局充足相似,但他俩使用BooksService类的上下文成员,而不是开创一个新的(代码文件Books萨姆pleWithDI
/ BooksService.cs):

public class BooksService
{
  private readonly BooksContext _booksContext;
  public BooksService(BooksContext context)
  {
    _booksContext = context;
  }

  public async Task AddBooksAsync()
  {
    var b1 = new Book
    {
      Title ="Professional C# 5 and .NET 4.5.1",
      Publisher ="Wrox Press"
    };
    var b2 = new Book
    {
      Title ="Professional C# 2012 and .NET 4.5",
      Publisher ="Wrox Press"
    };
    var b3 = new Book
    {
      Title ="JavaScript for Kids",
      Publisher ="Wrox Press"
    };
    var b4 = new Book
    {
      Title ="Web Design with HTML and CSS",
      Publisher ="For Dummies"
    };
    _booksContext.AddRange(b1, b2, b3, b4);
    int records = await _booksContext.SaveChangesAsync();

    WriteLine($"{records} records added");
  }

  public void ReadBooks()
  {
    var books = _booksContext.Books;
    foreach (var b in books)
    {
      WriteLine($"{b.Title} {b.Publisher}");
    }
    WriteLine();
  }
} 

借助注入框架的容器在
InitializeServices
方法中初阶化。创制二个ServiceCollection实例,将BooksService类添加到此集聚中,并开始展览暂且生命周期管理。那样,每一趟请求该服务时都会实例化
ServiceCollection。对于注册Entity Framework和SQL
Server,能够用扩充方法AddEntityFramework,AddSqlServer和AddDbContext。
AddDbContext方法须要一个Action委托作为参数,在那之中接收到三个DbContextOptionsBuilder参数。有了该选项参数,能够动用UseSqlServer增加方法配置上下文。这里用Entity
Framework注册SQL
Server与上二个演示是接近的功力(代码文件BooksSampleWithDI /
Program.cs):

private void InitializeServices()
{
  const string ConnectionString =@"server= (localdb)\MSSQLLocalDb;database=Books;trusted_connection=true";
  var services = new ServiceCollection();
  services.AddTransient<BooksService>();
  services.AddEntityFramework()
    .AddSqlServer()
    .AddDbContext<BooksContext>(options =>
      options.UseSqlServer(ConnectionString));
  Container = services.BuildServiceProvider();
}

public IServiceProvider Container { get; private set; }

劳务的起先化以及BooksService的行使是从Main方法成功的。通过调用IServiceProvider的GetService方法来寻找BooksService(代码文件BooksSampleWithDI
/ Program.cs):

static void Main()
{
  var p = new Program();
  p.InitializeServices();

  var service = p.Container.GetService<BooksService>();
  service.AddBooksAsync().Wait();
  service.ReadBooks();
}

运作应用程序能够见到记录已添加到图书数据库中然后从中读取记录。

*注意
在第三1章“XAML应用程序的方式”中阅读有关正视注入和Microsoft.Framework.DependencyInjection包的更加多音信,还足以瞻仰第陆0章“ASP.NET
Core”和第6一章“ ASP.NET MVC“。*

行使注重注入  

Entity
Framework Core 1.0放置了对依赖注入的支撑。连接和SQL
Server选拔能够经过利用正视注入框架注入,而非定义和接下来使用DbContext派生类的SQL
Server连接。 

要翻开此操作,Books萨姆pleWithDI示例项目对上三个代码示例项目展开了改动。 

此示例使用以下注重项和命名空间:

  依赖项

NETStandard.Library
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.Framework.DependencyInjection 

  命名空间

Microsoft.EntityFrameworkCore
System.Linq
System.Threading.Tasks
static System.Console

BooksContext类今后看起来相当粗略,只需定义Books属性(代码文件BooksSampleWithDI
/ BooksContext.cs):

public class BooksContext: DbContext
{
  public DbSet<Book> Books { get; set; }
}

BooksService是行使BooksContext的新类。BooksContext通过注入构造函数注入。方法AddBooksAsync和ReadBooks与上一个示范中的那么些方法丰硕相像,但他俩选用BooksService类的上下文成员,而不是创立三个新的(代码文件BooksSampleWithDI
/ BooksService.cs):

public class BooksService
{
  private readonly BooksContext _booksContext;
  public BooksService(BooksContext context)
  {
    _booksContext = context;
  }

  public async Task AddBooksAsync()
  {
    var b1 = new Book
    {
      Title ="Professional C# 5 and .NET 4.5.1",
      Publisher ="Wrox Press"
    };
    var b2 = new Book
    {
      Title ="Professional C# 2012 and .NET 4.5",
      Publisher ="Wrox Press"
    };
    var b3 = new Book
    {
      Title ="JavaScript for Kids",
      Publisher ="Wrox Press"
    };
    var b4 = new Book
    {
      Title ="Web Design with HTML and CSS",
      Publisher ="For Dummies"
    };
    _booksContext.AddRange(b1, b2, b3, b4);
    int records = await _booksContext.SaveChangesAsync();

    WriteLine($"{records} records added");
  }

  public void ReadBooks()
  {
    var books = _booksContext.Books;
    foreach (var b in books)
    {
      WriteLine($"{b.Title} {b.Publisher}");
    }
    WriteLine();
  }
} 

依傍注入框架的容器在
InitializeServices
方法中初叶化。创设一个ServiceCollection实例,将BooksService类添加到此聚众中,并开始展览一时半刻生命周期管理。那样,每一次请求该服务时都会实例化
ServiceCollection。对于注册Entity Framework和SQL
Server,能够用扩充方法AddEntityFramework,AddSqlServer和AddDbContext。
AddDbContext方法要求叁个Action委托作为参数,在那之中接收到三个DbContextOptionsBuilder参数。有了该选项参数,可以使用UseSqlServer扩张方法配置上下文。那里用Entity
Framework注册SQL
Server与上一个演示是看似的效果(代码文件Books萨姆pleWithDI /
Program.cs):

private void InitializeServices()
{
  const string ConnectionString =@"server= (localdb)\MSSQLLocalDb;database=Books;trusted_connection=true";
  var services = new ServiceCollection();
  services.AddTransient<BooksService>();
  services.AddEntityFramework()
    .AddSqlServer()
    .AddDbContext<BooksContext>(options =>
      options.UseSqlServer(ConnectionString));
  Container = services.BuildServiceProvider();
}

public IServiceProvider Container { get; private set; }

劳动的早先化以及BooksService的应用是从Main方法成功的。通过调用I瑟维斯Provider的GetService方法来搜寻BooksService(代码文件BooksSampleWithDI
/ Program.cs):

static void Main()
{
  var p = new Program();
  p.InitializeServices();

  var service = p.Container.GetService<BooksService>();
  service.AddBooksAsync().Wait();
  service.ReadBooks();
}

运作应用程序能够看看记录已添加到图书数据库中然后从中读取记录。

*注意
在第三1章“XAML应用程序的方式”中读书有关注重注入和Microsoft.Framework.DependencyInjection包的越来越多音讯,还足以参见第40章“ASP.NET
Core”和第陆一章“ ASP.NET MVC“。*

创立模型  

本章的第壹个示例映射单个表。第一个例子展现了创立表之间的关联。在本节中央银行使C#代码创造数据库而从未动用SQL
DDL语句(或通过采纳设计器)成立数据库。 

以身作则应用程序MenusSample使用以下依赖项和命名空间:

  依赖项

NETStandard.Library
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer

  命名空间

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.ChangeTracking
System
System.Collections.Generic
System.ComponentModel.DataAnnotations
System.ComponentModel.DataAnnotations.Schema
System.Linq
System.Threading
System.Threading.Tasks
static System.Console

创建立模型型  

本章的首先个示例映射单个表。第三个例证展现了成立表之间的关系。在本节中利用C#代码创设数据库而未有接纳SQL
DDL语句(或通过运用设计器)创立数据库。 

示范应用程序MenusSample使用以下重视项和命名空间:

  依赖项

NETStandard.Library
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer

  命名空间

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.ChangeTracking
System
System.Collections.Generic
System.ComponentModel.DataAnnotations
System.ComponentModel.DataAnnotations.Schema
System.Linq
System.Threading
System.Threading.Tasks
static System.Console

始建关系

让我们起先创办1个模型。示例项目选拔MenuCard和Menu类型定义壹对多涉及。MenuCard包罗Menu对象的列表。那种关涉由List
<Menu>类型的Menu属性不难定义(代码文件MenusSample /
MenuCard.cs):

public class MenuCard
{
  public int MenuCardId { get; set; }
  public string Title { get; set; }
  public List<Menu> Menus { get; } = new List<Menu>();

  public override string ToString() => Title;
}

该关系也能够从另3个角度访问,菜单能够选取MenuCard属性访问Menu卡德。钦赐MenuCardId 属性去定义外键关系(代码文件Menus萨姆ple / Menu.cs):

public class Menu
{
  public int MenuId { get; set; }
  public string Text { get; set; }
  public decimal Price { get; set; }

  public int MenuCardId { get; set; }
  public MenuCard MenuCard { get; set; }

  public override string ToString() => Text;
}

到数据库的投射由MenusContext类达成。这一个类定义为与上四个左右文类型类似的类型,它只含有两个属性来映射多少个对象类型:属性Menus和MenuCards(代码文件MenusSamples
/ MenusContext.cs):

public class MenusContext: DbContext
{
  private const string ConnectionString = @"server=(localdb)\MSSQLLocalDb;" +     "Database=MenuCards;Trusted_Connection=True";
  public DbSet<Menu> Menus { get; set; }
  public DbSet<MenuCard> MenuCards { get; set; }

  protected override void OnConfiguring(DbContextOptionsBuilder  optionsBuilder)
  {
    base.OnConfiguring(optionsBuilder);
    optionsBuilder.UseSqlServer(ConnectionString);
  }
}

创办关系

让我们起先创设三个模子。示例项目应用MenuCard和Menu类型定义一对多涉及。MenuCard包蕴Menu对象的列表。那种关涉由List
<Menu>类型的Menu属性简单定义(代码文件MenusSample /
MenuCard.cs):

public class MenuCard
{
  public int MenuCardId { get; set; }
  public string Title { get; set; }
  public List<Menu> Menus { get; } = new List<Menu>();

  public override string ToString() => Title;
}

该关系也足以从另二个角度访问,菜单可以应用MenuCard属性访问MenuCard。钦点MenuCardId 属性去定义外键关系(代码文件MenusSample / Menu.cs):

public class Menu
{
  public int MenuId { get; set; }
  public string Text { get; set; }
  public decimal Price { get; set; }

  public int MenuCardId { get; set; }
  public MenuCard MenuCard { get; set; }

  public override string ToString() => Text;
}

到数据库的映照由MenusContext类完毕。那么些类定义为与上2个光景文类型类似的体系,它只含有四个属性来映射七个指标类型:属性Menus和Menu卡德s(代码文件Menus山姆ples
/ MenusContext.cs):

public class MenusContext: DbContext
{
  private const string ConnectionString = @"server=(localdb)\MSSQLLocalDb;" +     "Database=MenuCards;Trusted_Connection=True";
  public DbSet<Menu> Menus { get; set; }
  public DbSet<MenuCard> MenuCards { get; set; }

  protected override void OnConfiguring(DbContextOptionsBuilder  optionsBuilder)
  {
    base.OnConfiguring(optionsBuilder);
    optionsBuilder.UseSqlServer(ConnectionString);
  }
}

使用.NET CLI实行搬迁

要选用C#代码自动创设数据库,能够利用enet工具使用package
dotnet-ef扩充.NET
CLI工具。此软件包包蕴用于为搬迁创制C#代码的命令。通过设置dotnet-ef
NuGet包能够职分令可用。您能够透过从类型布局文件(代码文件MenusSample /
project.json)中的工具部分引用此软件包来安装它:

"tools": {
  "dotnet-ef":"1.0.0-*"
 }

ef命令提供以下命令:数据库、dbcontext和迁移。数据库命令用于将数据库升级到一定的迁徙意况。
dbcontext命令列出项目中的全数DbContext派生类型(dbcontext
list),并从数据库(dbcontext scaffold)创造上下文和实业。
migrations命令则开创和删除迁移,以及开创SQL脚本去创立包罗全数迁移的数据库。如若生产数据库只可以从SQL管理员使用SQL代码创制和改动,能够将转移的本子移交给SQL管理员。 

为了成立起来迁移以从代码创建数据库,能够从开发人士命令提醒符调用以下命令,该命令创造名字为InitMenuCards的动员搬迁:

>dotnet ef migrations add InitMenuCards

命令migrations
add使用反射以及相反的引用模型访问DbContext派生类。此新闻创立五个类来创立和立异数据库。使用Menu,Menu卡德和MenusContext类创造四个类,MenusContextModelSnapshot和InitMenuCards。命令成功后得以在Migrations文件夹中找到那二种档次。

MenusContextModelSnapshot类包涵构建数据库的模子的当下情状:

[DbContext(typeof(MenusContext))]
partial class MenusContextModelSnapshot: ModelSnapshot
{
  protected override void BuildModel(ModelBuilder modelBuilder)
  {
    modelBuilder
     .HasAnnotation("ProductVersion","7.0.0-rc1-16348")
     .HasAnnotation("SqlServer:ValueGenerationStrategy",
       SqlServerValueGenerationStrategy.IdentityColumn);

     modelBuilder.Entity("MenusSample.Menu", b =>
     {
       b.Property<int>("MenuId")
        .ValueGeneratedOnAdd();
       b.Property<int>("MenuCardId");
       b.Property<decimal>("Price");
       b.Property<string>("Text");
       b.HasKey("MenuId");
     });

     modelBuilder.Entity("MenusSample.MenuCard", b =>
     {
       b.Property<int>("MenuCardId")
        .ValueGeneratedOnAdd();

       b.Property<string>("Title");
       b.HasKey("MenuCardId");
     });
     modelBuilder.Entity("MenusSample.Menu", b =>
     {
       b.HasOne("MenusSample.MenuCard")
        .WithMany()
        .HasForeignKey("MenuCardId");
     });
  }
}

InitMenuCards类定义了Up和Down方法。
Up方法列出了创办MenuCard和菜单表所需的全体操作,包含主键、列和关系。
Down方法删除多个表:

public partial class InitMenuCards: Migration
{
  protected override void Up(MigrationBuilder migrationBuilder)
  {
    migrationBuilder.CreateTable(
      name:"MenuCard",
      columns: table => new
      {
        MenuCardId = table.Column<int>(nullable: false)
          .Annotation("SqlServer:ValueGenerationStrategy",
            SqlServerValueGenerationStrategy.IdentityColumn),
        Title = table.Column<string>(nullable: true)
      },
      constraints: table =>
      {
        table.PrimaryKey("PK_MenuCard", x => x.MenuCardId);
      });

    migrationBuilder.CreateTable(
      name:"Menu",
      columns: table => new
      {
        MenuId = table.Column<int>(nullable: false)
          .Annotation("SqlServer:ValueGenerationStrategy",
            SqlServerValueGenerationStrategy.IdentityColumn),
        MenuCardId = table.Column<int>(nullable: false),
        Price = table.Column<decimal>(nullable: false),
        Text = table.Column<string>(nullable: true)
      },
      constraints: table =>
      {
        table.PrimaryKey("PK_Menu", x => x.MenuId);
        table.ForeignKey(
          name:"FK_Menu_MenuCard_MenuCardId",
          column: x => x.MenuCardId,
          principalTable:"MenuCard",
          principalColumn:"MenuCardId",
          onDelete: ReferentialAction.Cascade);
      });
  }

  protected override void Down(MigrationBuilder migrationBuilder)
  {
    migrationBuilder.DropTable("Menu");
    migrationBuilder.DropTable("MenuCard");
  }
}

注意 正在开始展览的各种更改都可以创立另2个搬迁。新搬迁仅定义从先前版本到新本子所需的变动。假使客户的数据库须求从随机早期的版本更新,迁移数据库时调用供给的迁徙。 

在支付进程中,也行不要求全部的动员搬迁,大概要求从品种中开创,因为或许未有该类一时气象的数据仓库储存在。在那种气象下可以去除迁移并创制二个较大的新搬迁。

使用.NET CLI举行搬迁

要运用C#代码自动创立数据库,能够利用enet工具使用package
dotnet-ef增添.NET
CLI工具。此软件包包涵用于为搬迁创设C#代码的授命。通过安装dotnet-ef
NuGet包能够任务令可用。您能够透过从类型计划文件(代码文件Menus萨姆ple /
project.json)中的工具部分引用此软件包来安装它:

"tools": {
  "dotnet-ef":"1.0.0-*"
 }

ef命令提供以下命令:数据库、dbcontext和迁移。数据库命令用于将数据库升级到一定的迁徙境况。
dbcontext命令列出项目中的全部DbContext派生类型(dbcontext
list),并从数据库(dbcontext scaffold)创立上下文和实业。
migrations命令则成立和删除迁移,以及开创SQL脚本去创制包括全体迁移的数据库。如若生产数据库只可以从SQL管理员使用SQL代码创立和改动,能够将扭转的本子移交给SQL管理员。 

为了创建起来迁移以从代码成立数据库,能够从开发人士命令提醒符调用以下命令,该命令创造名称为InitMenuCards的动员搬迁:

>dotnet ef migrations add InitMenuCards

澳门葡京备用网址,一声令下migrations
add使用反射以及相反的引用模型访问DbContext派生类。此新闻创设五个类来创立和创新数据库。使用Menu,MenuCard和MenusContext类成立四个类,MenusContextModelSnapshot和InitMenuCards。命令成功后能够在Migrations文件夹中找到那二种档次。

MenusContextModelSnapshot类蕴涵塑造数据库的模子的此时此刻情状:

[DbContext(typeof(MenusContext))]
partial class MenusContextModelSnapshot: ModelSnapshot
{
  protected override void BuildModel(ModelBuilder modelBuilder)
  {
    modelBuilder
     .HasAnnotation("ProductVersion","7.0.0-rc1-16348")
     .HasAnnotation("SqlServer:ValueGenerationStrategy",
       SqlServerValueGenerationStrategy.IdentityColumn);

     modelBuilder.Entity("MenusSample.Menu", b =>
     {
       b.Property<int>("MenuId")
        .ValueGeneratedOnAdd();
       b.Property<int>("MenuCardId");
       b.Property<decimal>("Price");
       b.Property<string>("Text");
       b.HasKey("MenuId");
     });

     modelBuilder.Entity("MenusSample.MenuCard", b =>
     {
       b.Property<int>("MenuCardId")
        .ValueGeneratedOnAdd();

       b.Property<string>("Title");
       b.HasKey("MenuCardId");
     });
     modelBuilder.Entity("MenusSample.Menu", b =>
     {
       b.HasOne("MenusSample.MenuCard")
        .WithMany()
        .HasForeignKey("MenuCardId");
     });
  }
}

InitMenu卡德s类定义了Up和Down方法。
Up方法列出了创办MenuCard和菜单表所需的兼具操作,包蕴主键、列和涉嫌。
Down方法删除五个表:

public partial class InitMenuCards: Migration
{
  protected override void Up(MigrationBuilder migrationBuilder)
  {
    migrationBuilder.CreateTable(
      name:"MenuCard",
      columns: table => new
      {
        MenuCardId = table.Column<int>(nullable: false)
          .Annotation("SqlServer:ValueGenerationStrategy",
            SqlServerValueGenerationStrategy.IdentityColumn),
        Title = table.Column<string>(nullable: true)
      },
      constraints: table =>
      {
        table.PrimaryKey("PK_MenuCard", x => x.MenuCardId);
      });

    migrationBuilder.CreateTable(
      name:"Menu",
      columns: table => new
      {
        MenuId = table.Column<int>(nullable: false)
          .Annotation("SqlServer:ValueGenerationStrategy",
            SqlServerValueGenerationStrategy.IdentityColumn),
        MenuCardId = table.Column<int>(nullable: false),
        Price = table.Column<decimal>(nullable: false),
        Text = table.Column<string>(nullable: true)
      },
      constraints: table =>
      {
        table.PrimaryKey("PK_Menu", x => x.MenuId);
        table.ForeignKey(
          name:"FK_Menu_MenuCard_MenuCardId",
          column: x => x.MenuCardId,
          principalTable:"MenuCard",
          principalColumn:"MenuCardId",
          onDelete: ReferentialAction.Cascade);
      });
  }

  protected override void Down(MigrationBuilder migrationBuilder)
  {
    migrationBuilder.DropTable("Menu");
    migrationBuilder.DropTable("MenuCard");
  }
}

注意 正在开始展览的每一种更改都足以创制另叁个搬迁。新搬迁仅定义从从前版本到新本子所需的变动。若是客户的数据库要求从随机早期的本子更新,迁移数据库时调用要求的搬迁。 

在支付进度中,也行不需求持有的迁移,大概要求从连串中创制,因为恐怕未有该类一时半刻气象的数据仓库储存在。在那种景色下得以去除迁移并创建2个较大的新搬迁。

选取MSBuild进行搬迁  

假定您正在利用基于MSBuild的项目Entity
Framework迁移而不是DNX,迁移命令是例外的。使用完全框架控制台应用程序、WPF应用程序或ASP.NET
四.陆类型项目,必要在NuGet包管理器控制毕尔巴鄂内定迁移命令,而不是开发职员命令提醒符。从Visual
Studio通过 工具➪库管理器控制台➪包管理器控制台
运维包管理器控制台。

在包管理器控制台能够接纳PowerShell脚本添加和删除迁移。命令如下

> Add-Migration InitMenuCards

创办贰个Migrations文件夹,个中带有如前所示的迁移类。

始建数据库 

乘势迁移类型形成,能够创建数据库。
DbContext派生类MenusContext包括1个再次来到DatabaseFacade对象的Database属性。使用DatabaseFacade可以成立和删除数据库。假诺数据库不存在,EnsureCreated方法会创设数据库;如若数据库已存在,则不实施此外操作。方法EnsureDeletedAsync删除数据库。以下代码片段创立数据库(虽然它不设有)(代码文件MenusSample
/ Program.cs):

private static async Task CreateDatabaseAsync()
{
  using (var context = new MenusContext())
  {
bool created = await context.Database.EnsureCreatedAsync();
    string createdText = created ?"created":"already exists";
    WriteLine($"database {createdText}");
  }
}

注意 如果数据仓库储存在可是一个较旧的结构版本,EnsureCreatedAsync方法不会接纳结构改变。那时能够经过调用Migrate方法来拓展结构升级。
Migrate是Microsoft.Data.Entity命名空间中定义的DatabaseFacade类的扩张方法。

运行程序将开创表MenuCard和Menu。基于暗中同意约定,表与实业类型是同样的名号。另二个预订用于创立主键:MenuCardId列会被定义为主键,因为属性名以Id停止。

CREATE TABLE [dbo].[MenuCard] (
  [MenuCardId] INT            IDENTITY (1, 1) NOT NULL,
  [Title]      NVARCHAR (MAX) NULL,
  CONSTRAINT [PK_MenuCard] PRIMARY KEY CLUSTERED ([MenuCardId] ASC)
);

Menu表定义了MenuCardId,它是MenuCard表的外键。由于DELETE
CASCADE,删除MenuCard也会删除全数涉及的Menu行:

CREATE TABLE [dbo].[Menu] (
  [MenuId]     INT             IDENTITY (1, 1) NOT NULL,
  [MenuCardId] INT             NOT NULL,
  [Price]      DECIMAL (18, 2) NOT NULL,
  [Text]       NVARCHAR (MAX)  NULL,
  CONSTRAINT [PK_Menu] PRIMARY KEY CLUSTERED ([MenuId] ASC),
  CONSTRAINT [FK_Menu_MenuCard_MenuCardId] FOREIGN KEY ([MenuCardId])
  REFERENCES [dbo].[MenuCard] ([MenuCardId]) ON DELETE CASCADE
);

在开立代码中有一些片段改变是立见成效的。例如,Text
和 Title 列的轻重能够从NVARAV4CHA福睿斯(MAX)减小,SQL
Server定义了可用以Price列的Money类型,并且组织名称能够从dbo更改。 Entity
Framework提供了三个挑选从代码中履行这几个改动:数据批注和Fluent
API,上面将切磋。

数据批注

潜移默化生成的数据库的1种格局是向实体类型丰富数据注释。能够应用Table属性更改表的称谓。要改成结构名称,Table属性定义Schema属性。假使要为字符串类型钦定不一致的尺寸,能够选择马克斯Length属性(代码文件MenusWithDataAnnotations
/ Menu卡德.cs):

[Table("MenuCards", Schema ="mc")]
public class MenuCard
{
  public int MenuCardId { get; set; }
  [MaxLength(120)]
  public string Title { get; set; }
  public List<Menu> Menus { get; }
}

Menu类的Table和马克斯Length属性同样可以使用。使用Column属性更改SQL类型(代码文件MenusWithDataAnnotations
/ Menu.cs):

[Table("Menus", Schema ="mc")]
public class Menu
{
  public int MenuId { get; set; }
  [MaxLength(50)]
  public string Text { get; set; }
  [Column(TypeName ="Money")]
  public decimal Price { get; set; }
  public int MenuCardId { get; set; }
  public MenuCard MenuCard { get; set; }
}

运用迁移创立数据库后得以看来结构名称下表的新名称,以及Title、Text
和 Price 字段中已更改的数据类型:

CREATE TABLE [mc].[MenuCards] (
  [MenuCardId] INT            IDENTITY (1, 1) NOT NULL,
  [Title]      NVARCHAR (120) NULL,
  CONSTRAINT [PK_MenuCard] PRIMARY KEY CLUSTERED ([MenuCardId] ASC)
);

CREATE TABLE [mc].[Menus] (
  [MenuId]     INT           IDENTITY (1, 1) NOT NULL,
  [MenuCardId] INT           NOT NULL,
  [Price]      MONEY         NOT NULL,
  [Text]       NVARCHAR (50) NULL,
  CONSTRAINT [PK_Menu] PRIMARY KEY CLUSTERED ([MenuId] ASC),
  CONSTRAINT [FK_Menu_MenuCard_MenuCardId] FOREIGN KEY ([MenuCardId])
    REFERENCES [mc].[MenuCards] ([MenuCardId]) ON DELETE CASCADE
);

使用MSBuild举行搬迁  

只要您正在利用基于MSBuild的项目Entity
Framework迁移而不是DNX,迁移命令是差异的。使用完全框架控制台应用程序、WPF应用程序或ASP.NET
四.陆体系体系,要求在NuGet包管理器控制马赛钦定迁移命令,而不是开发人士命令提示符。从Visual
Studio通过 工具➪库管理器控制台➪包管理器控制台
运转包管理器控制台。

在包管理器控制台可以选用PowerShell脚本添加和删除迁移。命令如下

> Add-Migration InitMenuCards

成立3个Migrations文件夹,个中包蕴如前所示的迁移类。

创制数据库 

随着迁移类型形成,能够创造数据库。
DbContext派生类MenusContext包罗一个再次回到DatabaseFacade对象的Database属性。使用DatabaseFacade能够创建和删除数据库。借使数据库不存在,EnsureCreated方法会创设数据库;借使数据库已存在,则不履行其余操作。方法EnsureDeletedAsync删除数据库。以下代码片段创设数据库(即便它不设有)(代码文件MenusSample
/ Program.cs):

private static async Task CreateDatabaseAsync()
{
  using (var context = new MenusContext())
  {
bool created = await context.Database.EnsureCreatedAsync();
    string createdText = created ?"created":"already exists";
    WriteLine($"database {createdText}");
  }
}

注意 一旦数据库存在然则二个较旧的协会版本,EnsureCreatedAsync方法不会接纳结构改变。这时能够因而调用Migrate方法来拓展结构升级。
Migrate是Microsoft.Data.Entity命名空间中定义的DatabaseFacade类的恢宏方法。

运行程序将开创表MenuCard和Menu。基于暗许约定,表与实业类型是一模1样的名号。另二个预订用于创立主键:MenuCardId列会被定义为主键,因为属性名以Id甘休。

CREATE TABLE [dbo].[MenuCard] (
  [MenuCardId] INT            IDENTITY (1, 1) NOT NULL,
  [Title]      NVARCHAR (MAX) NULL,
  CONSTRAINT [PK_MenuCard] PRIMARY KEY CLUSTERED ([MenuCardId] ASC)
);

Menu表定义了MenuCardId,它是MenuCard表的外键。由于DELETE
CASCADE,删除MenuCard也会去除全数涉及的Menu行:

CREATE TABLE [dbo].[Menu] (
  [MenuId]     INT             IDENTITY (1, 1) NOT NULL,
  [MenuCardId] INT             NOT NULL,
  [Price]      DECIMAL (18, 2) NOT NULL,
  [Text]       NVARCHAR (MAX)  NULL,
  CONSTRAINT [PK_Menu] PRIMARY KEY CLUSTERED ([MenuId] ASC),
  CONSTRAINT [FK_Menu_MenuCard_MenuCardId] FOREIGN KEY ([MenuCardId])
  REFERENCES [dbo].[MenuCard] ([MenuCardId]) ON DELETE CASCADE
);

在开立代码中有一些片段改变是可行的。例如,Text
和 Title 列的大小能够从NVA兰德TucsonCHAGL450(MAX)减小,SQL
Server定义了可用以Price列的Money类型,并且组织名称能够从dbo更改。 Entity
Framework提供了四个挑选从代码中推行这一个改动:数据批注和Fluent
API,上边将研究。

数量批注

影响生成的数据库的一种办法是向实体类型丰裕数据注释。能够利用Table属性更改表的称呼。要转移结构名称,Table属性定义Schema属性。要是要为字符串类型钦定不一致的长度,能够运用马克斯Length属性(代码文件MenusWithDataAnnotations
/ Menu卡德.cs):

[Table("MenuCards", Schema ="mc")]
public class MenuCard
{
  public int MenuCardId { get; set; }
  [MaxLength(120)]
  public string Title { get; set; }
  public List<Menu> Menus { get; }
}

Menu类的Table和马克斯Length属性同样能够应用。使用Column属性更改SQL类型(代码文件MenusWithDataAnnotations
/ Menu.cs):

[Table("Menus", Schema ="mc")]
public class Menu
{
  public int MenuId { get; set; }
  [MaxLength(50)]
  public string Text { get; set; }
  [Column(TypeName ="Money")]
  public decimal Price { get; set; }
  public int MenuCardId { get; set; }
  public MenuCard MenuCard { get; set; }
}

行使迁移创立数据库后能够见到结构名称下表的新名称,以及Title、Text
和 Price 字段中已更改的数据类型:

CREATE TABLE [mc].[MenuCards] (
  [MenuCardId] INT            IDENTITY (1, 1) NOT NULL,
  [Title]      NVARCHAR (120) NULL,
  CONSTRAINT [PK_MenuCard] PRIMARY KEY CLUSTERED ([MenuCardId] ASC)
);

CREATE TABLE [mc].[Menus] (
  [MenuId]     INT           IDENTITY (1, 1) NOT NULL,
  [MenuCardId] INT           NOT NULL,
  [Price]      MONEY         NOT NULL,
  [Text]       NVARCHAR (50) NULL,
  CONSTRAINT [PK_Menu] PRIMARY KEY CLUSTERED ([MenuId] ASC),
  CONSTRAINT [FK_Menu_MenuCard_MenuCardId] FOREIGN KEY ([MenuCardId])
    REFERENCES [mc].[MenuCards] ([MenuCardId]) ON DELETE CASCADE
);

Fluent API  

潜移默化创设的表的另一种方式是应用Fluent
API中DbContext派生类的OnModelCreating方法。它的长处是足以保证实体类型大致,而不添加别的性质,Fluent
API还提供了比使用质量更加多的选项。 

以下代码片段展现了BooksContext类重写OnModelCreating方法。作为参数接收的ModelBuilder类提供了有的措施,并定义了两种扩充方法。
HasDefaultSchema是中间四个扩张方法,它将默许结构选择于当下拥有品类的模子。
Entity方法重回一个EntityTypeBuilder,使你能够自定义实体,例如将其映射到特定的表名和定义键和目录(代码文件MenusSample
/ MenusContext.cs):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  base.OnModelCreating(modelBuilder);

  modelBuilder.HasDefaultSchema("mc");

  modelBuilder.Entity<MenuCard>()
    .ToTable("MenuCards")
    .HasKey(c => c.MenuCardId);

  // etc.

  modelBuilder.Entity<Menu>()
    .ToTable("Menus")
    .HasKey(m => m.MenuId);

  // etc.
}

EntityTypeBuilder定义了二个Property方法来布局属性。
Property方法重回PropertyBuilder,能够依次配置具有最大尺寸值,须要的装置和SQL类型的品质,并点名是不是应自动生成值(例如标识列):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  // etc.

  modelBuilder.Entity<MenuCard>()
    .Property<int>(c => c.MenuCardId)
    .ValueGeneratedOnAdd();

  modelBuilder.Entity<MenuCard>()
    .Property<string>(c => c.Title)
    .HasMaxLength(50);

  modelBuilder.Entity<Menu>()
    .Property<int>(m => m.MenuId)
    .ValueGeneratedOnAdd();

  modelBuilder.Entity<Menu>()
.Property<string>(m => m.Text)
    .HasMaxLength(120);

  modelBuilder.Entity<Menu>()
    .Property<decimal>(m => m.Price)
    .HasColumnType("Money");

  // etc.
} 

EntityTypeBuilder定义映射方法去定义壹对多映射。HasMany
结合 WithOne 方法定义了多Menus 和3个Menu 卡德 的映照。
HasMany供给与WithOne链接,即HasOne方法必要贰个带WithMany或WithOne的链。链接
HasOne 和
WithMany定义了1对多关系,链接HasOne与WithOne定义了格外的关系:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  // etc.

  modelBuilder.Entity<MenuCard>()
    .HasMany(c => c.Menus)
    .WithOne(m => m.MenuCard);
  modelBuilder.Entity<Menu>()
    .HasOne(m => m.MenuCard)
    .WithMany(c => c.Menus)
    .HasForeignKey(m => m.MenuCardId);
}

在OnModelCreating方法中创设映射之后方可成立如前所示的迁移。

Fluent API  

潜移默化创立的表的另一种方法是使用Fluent
API中DbContext派生类的OnModelCreating方法。它的优点是能够保持实体类型大约,而不添加其他性质,Fluent
API还提供了比使用品质愈多的选项。 

以下代码片段呈现了BooksContext类重写OnModelCreating方法。作为参数接收的ModelBuilder类提供了某个艺术,并定义了三种扩充方法。
HasDefaultSchema是中间3个恢弘方法,它将暗许结构选取于当下具备品种的模子。
Entity方法重回3个EntityTypeBuilder,使你能够自定义实体,例如将其映射到一定的表名和定义键和目录(代码文件MenusSample
/ MenusContext.cs):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  base.OnModelCreating(modelBuilder);

  modelBuilder.HasDefaultSchema("mc");

  modelBuilder.Entity<MenuCard>()
    .ToTable("MenuCards")
    .HasKey(c => c.MenuCardId);

  // etc.

  modelBuilder.Entity<Menu>()
    .ToTable("Menus")
    .HasKey(m => m.MenuId);

  // etc.
}

EntityTypeBuilder定义了三个Property方法来铺排属性。
Property方法重返PropertyBuilder,能够依次配置具有最大尺寸值,供给的设置和SQL类型的性质,并点名是不是应自动生成值(例如标识列):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  // etc.

  modelBuilder.Entity<MenuCard>()
    .Property<int>(c => c.MenuCardId)
    .ValueGeneratedOnAdd();

  modelBuilder.Entity<MenuCard>()
    .Property<string>(c => c.Title)
    .HasMaxLength(50);

  modelBuilder.Entity<Menu>()
    .Property<int>(m => m.MenuId)
    .ValueGeneratedOnAdd();

  modelBuilder.Entity<Menu>()
.Property<string>(m => m.Text)
    .HasMaxLength(120);

  modelBuilder.Entity<Menu>()
    .Property<decimal>(m => m.Price)
    .HasColumnType("Money");

  // etc.
} 

EntityTypeBuilder定义映射方法去定义壹对多映射。HasMany
结合 WithOne 方法定义了多Menus 和二个Menu Card 的投射。
HasMany要求与WithOne链接,即HasOne方法供给2个带WithMany或WithOne的链。链接
HasOne 和
WithMany定义了壹对多关系,链接HasOne与WithOne定义了一定的涉及:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  // etc.

  modelBuilder.Entity<MenuCard>()
    .HasMany(c => c.Menus)
    .WithOne(m => m.MenuCard);
  modelBuilder.Entity<Menu>()
    .HasOne(m => m.MenuCard)
    .WithMany(c => c.Menus)
    .HasForeignKey(m => m.MenuCardId);
}

在OnModelCreating方法中开创映射之后方可创设如前所示的迁徙。

从数据库创造模型  

从模型能够创建数据库,相反从数据库也足以创立模型。 

要从SQL
Server数据库执行此操作,除了其余包,还非得将NuGet包添加到DNX项目中,EntityFramework.MicrosoftSqlServer.Design。然后能够在开发人士命令提醒符使用以下命令:

> dnx ef dbcontext scaffold 
"server=(localdb)\MSSQLLocalDb;database=SampleDatabase; trusted_connection=true""EntityFramework.MicrosoftSqlServer"

dbcontext命令能够从品类中列出DbContext对象,同时也开创DBContext对象。命令scaffold成立DbContext派生类以及模型类。
dnx ef dbcontext scaffold
需求七个要求的参数:数据库的连日字符串和应用的提供程序。前面所示的口舌中,在SQL
Server(localdb)\
MSSQLLocalDb上访问数据库SampleDatabase。使用的提供程序是EntityFramework.MicrosoftSqlServer。那个NuGet包以及具有相同名称和布置后缀的NuGet包必须添加到项目中。 

运营此命令后,能够看来DbContext派生类以及变更的模子类型。暗中同意情形下,模型的安排使用fluent
API完毕。不过也能够将其变动为利用提供-a选项的数目批注。还是能影响生成的光景文类名称以及出口目录。只需使用采取-h检查分歧的可用选项。

 

—————-未完待续

从数据库创造模型  

从模型能够创造数据库,相反从数据库也足以创造模型。 

要从SQL
Server数据库执行此操作,除了别的包,还非得将NuGet包添加到DNX项目中,EntityFramework.MicrosoftSqlServer.Design。然后能够在开发职员命令提示符使用以下命令:

> dnx ef dbcontext scaffold 
"server=(localdb)\MSSQLLocalDb;database=SampleDatabase; trusted_connection=true""EntityFramework.MicrosoftSqlServer"

dbcontext命令能够从品种中列出DbContext对象,同时也开创DBContext对象。命令scaffold成立DbContext派生类以及模型类。
dnx ef dbcontext scaffold
必要四个须求的参数:数据库的连年字符串和采取的提供程序。前边所示的口舌中,在SQL
Server(localdb)\
MSSQLLocalDb上访问数据库SampleDatabase。使用的提供程序是EntityFramework.MicrosoftSqlServer。那些NuGet包以及有着相同名称和筹划后缀的NuGet包必须添加到项目中。 

启动此命令后,可以看看DbContext派生类以及变化的模型类型。暗许情形下,模型的配置利用fluent
API完毕。不过也得以将其改变为运用提供-a选项的数码批注。还足以影响生成的上下文类名称以及出口目录。只需选择选拔-h检查区别的可用选项。

 

—————-未完待续

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website