自动员搬迁移数据库的贯彻,高等编制程序

在付出涉及到数据库的主次时,常会赶过一开端设计的结构无法满足要求供给再增加新字段或新表的情状,这个时候就要求开展数据库迁移。
达成数据库迁移有很各样方法,从手动管理各种版本的ddl脚本,到得以达成本身的migrator,或是使用Entity Framework提供的Code First迁移作用。
Entity Framework提供的迁移成效能够满足抢先十分之四个人的须求,但仍会设有难以分品种管理迁移代码和易于现身"context has changed"错误的主题材料。

**译文,民用原创,转发请注脚出处(C# 6 与 .NET Core 1.0 高等编制程序 - 38 章 实体框架宗旨(上)卡塔 尔(英语:State of Qatar),不对的地点应接提议与调换。** 

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

附西班牙语版原来的文章:Professional C# 6 and .NET Core 1.0 - 38 Entity Framework Core

本章节译文分为上下篇,下篇见: C# 6 与 .NET Core 1.0 高端编制程序 - 38 章 实体框架宗旨(下)


本章内容

  • 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上选拔的章程。
可以成功增多实体字段后,只需刷新网页就可以把修正应用到数据库。

实体框架的野史

实体框架是提供实体到事关的酷炫的框架。通过这种措施,能够创立映射到数量库表的类型,使用LINQ成立数据库查询,创设和换代指标,并将它们写入数据库。 

由此长年累月对Entity Framework的少许改过,最新的版本是一个完全的重写。一齐来探访Entity Framework的历史,以致重写的原由。

  • Entity Framework 1—Entity Framework的首先个版本没有有备无患好与.NET 3.5同盟,但它高效就足以与.NET 3.5 SP1宽容。另三个成品LINQ to SQL提供了一些近乎的功用,且早就可用于.NET 3.5。 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 4 在.NET 4中相称,並且赢得了最首要改善,在那之中不菲源于LINQ到SQL的主见。由于变化超级大,版本2和3已被跳过。这些本子里扩大了推迟加载以得到访问属性的涉嫌。在选用SQL数据定义语言(DDL卡塔尔国设计模型之后,能够创设数据库。今后采用Entity Framework的七个模型是Database First或Model First。大概最要紧的风味是支撑轻便对象类(POCO卡塔尔,因而不再要求从基类EntityObject派生。

星彩网app下载,乘胜更新(举例Entity Framework 4.1,4.2卡塔尔,NuGet包扩充了额外的成效,由此能越来越快地抬高效果。 Entity Framework 4.1提供了Code First模型,个中用于定义映射的EDMX文件不再采纳。相反,全体的映照都选择C#代码定义

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

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

  • Entity Framework 5—Entity Framework 5的NuGet包援助.NET 4.5和.NET 4应用程序。不过,Entity Framework 5的多数效益都可用于.NET 4.5。 Entity Framework仍旧基于.NET 4.5在系统上安装的类型。此版本的新扩张效果与利益是性质改革以致支持新的SQL Server成效,比方空间数据类型。

  • Entity Framework 6—Entity Framework 6缓和了Entity Framework 5的一些难题,当中有的是安装在系统上的框架的一片段,后生可畏都部队分经过NuGet扩张提供。方今Entity Framework的百分百代码已移至NuGet包。为了不变成冲突,使用了三个新的命名空间。将应用程序移植到新本辰时,必须更动命名空间。

本书切磋Entity Framework的新式版本,Entity Framework Core 1.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 1.0不完全扶持Entity Framework 6提供的兼具机能。随着岁月的推移,Entity Framework的新本子将提供更加多职能,在乎所接受的Entity Framework的本子。即便采用Entity Framework 6 超级多强硬的理由,但在非Windows平台上应用ASP.NET Core 1.0、Entity Framework和通用Windows平台(UWP),甚至非关周到据存款和储蓄,都须要动用Entity Framework Core 1.0。 

本章介绍Entity Framework Core 1.0。从四个回顾的模子读取和SQL Server中写入音信开始,稍后会介绍增加关系,在写入数据库时将介绍更改追踪器和冲突管理。利用搬迁创制和修正数据库结构是本章的另四个重视部分。 

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

兑现机关迁移的思路

数据库迁移需求钦赐改动的一些,比方加多表和增进字段。
而落到实处全自动员搬迁移要求自动生成那些更动的片段,具体来讲须要

  • 得到数据库现成的组织
  • 得到代码中存活的构造
  • 比较之下结构之间的歧异并生成迁移

那多亏Entity Framework的Add-Migration(或dotnet ef migrations add)命令所做的事务,
接下去大家将看什么不应用这类的下令,在NHibernate, Entity Framework和Entity Framework Core中得以完成自动的拍卖。

实体框架简单介绍

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

在第叁个示范中,首先创设数据库。能够应用Visual Studio 2014中的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
)

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数据库的言传身教应用程序Book萨姆ple是二个调整台应用程序(Package卡塔尔。此示例使用以下信任项和命名空间:

  依赖项

NETStandard.Library
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer

   命名空间

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

 星彩网app下载 1

图 38.1  

Book类是叁个简单的实体类型,它定义了多个属性。 BookId属性映射到表的主键,Title属性指向题目列,Publisher属性指向Publisher列。Table属性应用于类型将项目映射到Books表(代码文件Books萨姆ple / Book.cs卡塔 尔(英语:State of Qatar):

[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 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数据库(代码文件BooksSample / 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唯有迁移记录多少个表。
创建完事后还要把带迁移记录的布局保留下去,用作前面包车型客车比较,假使这里不保留会导致迁移记录的再次创设错误。

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 每便迁移都会 1
  • 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卡塔 尔(英语:State of Qatar):

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对象能源微机查看写入到数据库的多少。

写在最后

机关迁移数据库如若对的运用,能够升高项目中种种模块的独立性,减少支出和布置的专门的学问量。
可是因为无法手动调节搬迁内容,有一定的局限和产品险,必要领会好应用的ORM迁移的特色。

从数据库读取

从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 Events窗口,能够阅览发送到数据库的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'

*注意 在第13章“语言集成查询”中详细商量了LINQ。
*

写在终极的广告

ZKWeb网页框架业已在实质上项目中接收了那项技能,目前来看迁移部分依旧对比牢固的。
那项工夫最先是为着插件商城而支出的,在下载安装插件现在没有供给重新编写翻译主程序,不须求奉行别的迁移命令就能够接收。
现阶段尽管从未达成插件商铺,也减小了成百上千平日支付的劳作。

假诺您风乐趣,款待参加ZKWeb交换群522083886生机勃勃并研究。

修正记录

只需改造已加载上下文的目的并调用SaveChangesAsync就能够轻松完成纠正记录(代码文件BooksSample / 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语句(代码文件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卡塔 尔(阿拉伯语:قطر‎在毫无在享有方案中都可用。使用示例代码不能有效地删除全体目的。您能够选取叁个SQL语句删除全部并不是逐一删除笔录。在第37章“ADO.NET”中表明了怎么成功那或多或少。*

刺探了什么样增多、查询、更新和删除记录,本章将介绍幕后的效能,并动用Entity Framework步向高级场景。

应用正视注入  

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属性(代码文件Books萨姆pleWithDI / 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卡塔 尔(英语:State of Qatar):

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();
}

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

*注意 在第31章“XAML应用程序的情势”中阅读有关注重注入和Microsoft.Framework.DependencyInjection包的越多消息,还是能崇敬第40章“ASP.NET Core”和第41章“ ASP.NET MVC“。*

创造模型  

本章的首先个示例映射单个表。第一个例证显示了创设表之间的关系。在本节中动用C#代码创制数据库而从未接收SQL DDL语句(或透过接受设计器卡塔 尔(英语:State of Qatar)成立数据库。 

演示应用程序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

创设关系

让大家最早创制八个模子。示例项目采用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类落成。这几个类定义为与上二个前后文类型相像的体系,它只含有七个属性来映射八个对象类型:属性Menus和Menu卡德s(代码文件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);
  }
}

使用.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,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");
  }
}

注意 正在进展的各样校正都足以成立另一个搬迁。新搬迁仅定义从在此以前版本到新本子所需的变动。借使客商的数据库必要从随机开始时期的版本更新,迁移数据库时调用须要的迁徙。 

在付出进程中,也行没有供给具有的搬迁,大概必要从类型中成立,因为大概未有该类有时气象的数据仓库储存在。在这里种情景下得以去除迁移并创设二个极大的新搬迁。

应用MSBuild进行搬迁  

假定你正在接纳基于MSBuild的项目Entity Framework迁移并不是DNX,迁移命令是差别的。使用完全框架控制台应用程序、WPF应用程序或ASP.NET 4.6连串类别,供给在NuGet包微处理器调整新竹内定迁移命令,实际不是开荒职员命令提醒符。从Visual Studio通过 工具➪库微电脑调节台➪包微处理器调整台 运行包微电脑调整台。

在包微型机调整台能够选用PowerShell脚本增多和删除迁移。命令如下

> Add-Migration InitMenuCards

创设多少个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类的增添方法。

运路程序将制造表Menu卡德和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 列的轻重缓急能够从NVARCHA凯雷德(MAX卡塔尔减小,SQL Server定义了可用以Price列的Money类型,並且组织名称能够从dbo修改。 Entity Framework提供了五个接收从代码中实行这几个改换:数据讲授和Fluent API,上边将商量。

多少疏解

潜濡默化生成的数据库的生龙活虎种艺术是向实体类型丰裕数据注释。能够选择Table属性纠正表的名称。要修改结构名称,Table属性定义Schema属性。若是要为字符串类型指定不一样的尺寸,能够动用马克斯Length属性(代码文件MenusWithDataAnnotations / MenuCard.cs卡塔 尔(英语:State of Qatar):

[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和MaxLength属性相像能够采取。使用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,令你可以自定义实体,比方将其映射到一定的表名和定义键和目录(代码文件Menus萨姆ple / MenusContext.cs卡塔 尔(英语:State of Qatar):

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方法须求二个带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上访问数据库萨姆pleDatabase。使用的提供程序是EntityFramework.MicrosoftSqlServer。这些NuGet包以至有着相符名称和统筹后缀的NuGet包必得加多到项目中。 

运维此命令后,能够见见DbContext派生类以至变化的模型类型。私下认可情形下,模型的安插利用fluent API达成。不过也能够将其更动为使用提供-a选项的数码教学。还足以影响生成的上下文类名称以致出口目录。只需选拔选取-h检查不一样的可用选项。

 

----------------未完待续

本文由星彩网app下载发布于计算机编程,转载请注明出处:自动员搬迁移数据库的贯彻,高等编制程序

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。