高等编程,使用HiLo生成主键

HiLo是在NHibernate中生成主键的一种办法,但是现在我们可以在Entity
Framework Core中利用。所以在这篇内容中,我将向你在介绍咋样在Entity
Framework Core中行使HiLo生成主键。

**译文,民用原创,转载请表明出处(C# 6 与 .NET Core 1.0 高级编程 – 38 章 实体框架核心(上)),不对的地点欢迎提出与互换。** 

章节出自《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 

什么是Hilo?

HiLo是High Low的简写,翻译成中文叫高低位形式。

HiLo是由“Hi”和“Lo”两有些生成主键的一种格局。“Hi”部分源于数据库,“Lo”部分在内存中生成以创设唯一值。请牢记,“Lo”是一个限制数字,如0-100。由此,当“Hi”部分用完“Lo”范围时,再一次展开数据库调用以博得下一个“Hi数字”。从而HiLo情势的长处在于你事先可以通晓主键的值,而不用每一回都与数库据暴发相互

总结有以下四点:

  1. “Hi”部分由数据库分配,六个冒出请求保管收获唯一的连接值;
  2. 要是得到“Hi”部分,我们还亟需精通“incrementSize”的值(“Lo”条目标数据);
    “Lo”取的限量:[0,incrementSize];
  3. 标识范围的公式是:(Hi – 1) * incrementSize) + 1(Hi –
    1) * incrementSize) + incrementSize)
  4. 当有着“Lo”值使用完时,需要再行从数据库中取出一个新的“Hi”值,并将“Lo”部分重置为0。

在这里演示在两个冒出事务中的例子,每个业务插入三个实体:

lovebet爱博体育 1

实业框架的野史

实体框架是提供实体到事关的映射的框架。通过这种艺术,可以成立映射到数量库表的体系,使用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派生。

乘机更新(例如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

Sql Server 序列

在EF Core中采取HiLo生成主键,我们还亟需了然Sql
Server中一个定义序列(Sequence)

队列是在SQL Server
2012中引入的(但是Oracle很已经已经落实了http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_6015.htm)。连串是用户定义的目的,它按照创制的属性生成一层层数值。它与
Identity 列相似,但它们之间有不少不同之处。例如,

  • 队列用于转移数据库范围的系列号;
  • 队列不与一个表相关联,您可以将其与三个表相关联;
  • 它可以用于插入语句来插入标识值,也得以在T-SQL脚本中使用。

创办连串示例的SQL语句:

Create Sequence [dbo].[Sequence_Test] 
As [BigInt]         --整数类型
Start With 1        --起始值
Increment By 1      --增量值
MinValue 1          --最小值
MaxValue 9999999    --最大值
Cycle               --达到最值循环 [ CYCLE | NO CYCLE ]
Cache  5;           --每次取出5个值缓存使用 [ CACHE [<常量>] | NO CACHE ]

利用示例:

Create Table #T(Id BigInt Primary Key,[Time] DateTime);

Insert Into #T
            ( Id , Time )
Values      ( NEXT VALUE FOR [dbo].[Sequence_Test] , -- Id - bigint
              GetDate()  -- Time - datetime
              )
Go 10


Select * From #T

询问结果:

Id Time
1 2017-11-23 16:46:50.613
2 2017-11-23 16:46:50.643
3 2017-11-23 16:46:50.667
4 2017-11-23 16:46:50.677
5 2017-11-23 16:46:50.687
6 2017-11-23 16:46:50.697
7 2017-11-23 16:46:50.707
8 2017-11-23 16:46:50.717
9 2017-11-23 16:46:50.730
10 2017-11-23 16:46:50.740

有关连串更多的情节,可以查阅如下材料:

实业框架简介

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

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

选料Books数据库中的表节点,然后选用”添加新表”来创造表Books。使用图38.1中所示的设计器,或者通过在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
)

行使HiLo生成主键

让我们看看哪些运用HiLo在Entity Framework Core中生成主键。

为了演示,我们成立了四个没有关联的实体。

    public class Category
    {
        public int CategoryID { get; set; }
        public string CategoryName { get; set; }
    }

    public class Product
    {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
    }

请牢记,EF Core按常规配置一个名为Id<type
name>Id
作为实体的主键属性。现在我们需要创设大家的DBContext,在此间大家创设SampleDBContext.cs类:

public class SampleDBContext : DbContext
{
    public SampleDBContext()
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionbuilder)
    {
            var sqlConnectionStringBuilder = new SqlConnectionStringBuilder {
                DataSource = "****",
                InitialCatalog = "EFSampleDB",
                UserID = "sa",
                Password = "***"
            };
            optionsBuilder.UseSqlServer(sqlConnectionStringBuilder.ConnectionString);

    }

    protected override void OnModelCreating(ModelBuilder modelbuilder)
    {
        modelbuilder.ForSqlServerUseSequenceHiLo("DBSequenceHiLo");
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
}
  • SampleDBContext构造函数开头化数据库,类型于EF
    6中的DropCreateDatabaseAlways
  • OnConfiguring() 方法用于配置数据库链接字符串;
  • OnModelCreating措施用于定义实体模型。要定义HiLo系列,请使用ForSqlServerUseSequenceHiLo增加方法。您需要提供系列的名目。

运作应用程序,您应该在开立“EFSampleDB”数据库中看出Product表、Category表和DBSequenceHiLo序列。

lovebet爱博体育 2

以下是开创DBSequenceHiLo的脚本。

Create Sequence [dbo].[DBSequenceHiLo] 
 As [BigInt]
 Start With 1
 Increment By 10
 MinValue -9223372036854775808
 MaxValue 9223372036854775807
 Cache 
Go

正如你所见到的,它从1从头,递增是10。

现今向数据库中添加一些多少。以下代码首先添加3个Category实业和调用SaveChanges(),然后添加3个Product实体并调用SaveChanges()

    using (var dataContext = new SampleDBContext())
    {
        dataContext.Categories.Add(new Category() { CategoryName = "Clothing" });
        dataContext.Categories.Add(new Category() { CategoryName = "Footwear" });
        dataContext.Categories.Add(new Category() { CategoryName = "Accessories" });
        dataContext.SaveChanges();
        dataContext.Products.Add(new Product() { ProductName = "TShirts" });
        dataContext.Products.Add(new Product() { ProductName = "Shirts" });
        dataContext.Products.Add(new Product() { ProductName = "Causal Shoes" });
        dataContext.SaveChanges();
    }

当以此代码第一次被执行,Clothing
实体通过Add方法扩大到DBContext时,就会向数据库调用获取系列的值,大家也可以因此SQL
Server Profiler来表达它。
lovebet爱博体育 3

次调用dataContext.SaveChanges()时,3个Category实体将被保存。查看执行的SQL语句。主键值已经被转移,系列值的得到也只举行了一遍。
lovebet爱博体育 4

就是插入3个Product实业,系列值也不会从数据库中拿走。唯有当插入10条记下(Lo部分耗尽)时,才会向数据库调用得到下一个(Hi部分)体系值。

创立模型 

用以访问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

 lovebet爱博体育 5


38.1
  

Book类是一个大概的实体类型,它定义了三个特性。
BookId属性映射到表的主键,Title属性指向标题列,Publisher属性指向Publisher列。Table属性应用于类型将项目映射到Books表(代码文件Books山姆ple
/ Book.cs):

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

向HiLo运用到单个实体

地方的代码六个表共用一个HiLo连串。假使你只想针对一个一定的表,那么您可以行使下边的代码。

    modelbuilder.Entity<Category>().
            Property(o => o.CategoryID).ForSqlServerUseSequenceHiLo();

这段代码将开创一个默认名称为“EntityFrameworkHiLoSequence”的新连串,因为从没点名名字。您也足以定义四个HiLo体系。例如:

    protected override void OnModelCreating(ModelBuilder modelbuilder)
    {
        modelbuilder.ForSqlServerUseSequenceHiLo("DBSequenceHiLo");
        modelbuilder.Entity<Category>()
                .Property(o => o.CategoryID).ForSqlServerUseSequenceHiLo();
    }

在数据库中,将创建五个系列。Category实体将利用EntityFrameworkHiLoSequence序号,所有其他实体使用DBSequenceHiLo序列。

lovebet爱博体育 6

创立上下文 

开创的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);
  }
}

概念连接字符串的另一个选项是行使倚重注入,将在本章前边介绍。 

配置HiLo序列

ForSqlServerHasSequence增添方法不可以更改先导值和增量值的选项。可是,有一种方法来定义这一个采用。首先,使用HasSequence方法定义连串的StartAtIncrementBy选拔,然后再利用ForSqlServerUseSequenceHiLo()扩张方法,要维持体系的称呼相同。例如:

    modelbuilder.HasSequence<int>("DBSequenceHiLo")
                      .StartsAt(1000).IncrementsBy(5);
    modelbuilder.ForSqlServerUseSequenceHiLo("DBSequenceHiLo");

在这种状态下,生成DBSequenceHiLo的剧本如下。

CREATE SEQUENCE [dbo].[DBSequenceHiLo] 
 AS [int]
 START WITH 1000
 INCREMENT BY 5
 MINVALUE -2147483648
 MAXVALUE 2147483647
 CACHE 
GO

故而当大家实施同样的代码插入3个Category实体,那么主键的值将从1000从头。

lovebet爱博体育 7

同时由于IncrementBy拔取设置为“5”,所以当在上下文中添加第6个插入时,将开展数据库调用以博取下一个连串值。以下是插入3个Category实体然后插入3个的Product实体时SQL
Server
profiler的屏幕截图,您可以看来数据库调用获取序列的下一个值的次数是2次。
lovebet爱博体育 8

一经你对在Entity Framework
Core中动用HiLo生成主键感兴趣,不防自己入手测试一下。

参考资料:

写入数据库

最近已开立了有Books表的数据库,也定义了模型和左右文类,然后可以用数码填充表。创设AddBookAsync方法将Book对象添加到数据库。首先,BooksContext对象被实例化,这里运用using语句确保数据库连接关闭。使用Add方法将对象添加到上下文之后,实体被写入调用SaveChangesAsync的数据库(代码文件Books山姆(Sam)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山姆(Sam)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对象资源管理器查看写入到数据库的数量。

从数据库读取

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

在调节期间打开
AMDliTrace 伊夫(Eve)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'

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

更新记录

只需变更已加载上下文的对象并调用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山姆(Sam)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语句删除所有而不是逐条删除笔录。在第37章“ADO.NET”中解释了如何完成这或多或少。*

问询了哪些添加、查询、更新和删除记录,本章将介绍幕后的效能,并利用Entity
Framework进入高级场景。

采取看重注入  

Entity
Framework Core 1.0放置了对倚重注入的辅助。连接和SQL
Server选取可以透过利用依赖注入框架注入,而非定义和接下来使用DbContext派生类的SQL
Server连接。 

要翻看此操作,Books山姆(Sam)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; }
}

Books瑟维斯(Service)(Service)是使用BooksContext的新类。BooksContext通过注入构造函数注入。方法AddBooksAsync和ReadBooks与上一个演示中的这个点子异常相似,但他们采用Books瑟维斯(Service)(Service)类的上下文成员,而不是创立一个新的(代码文件Books萨姆(Sam)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();
  }
} 

借助注入框架的容器在
Initialize瑟维斯(Service)s
方法中起始化。创设一个Service(Service)Collection实例,将BooksService类添加到此汇聚中,并举办暂时生命周期管理。这样,每一回请求该服务时都会实例化
瑟维斯(Service)Collection。对于注册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方法成功的。通过调用I瑟维斯(Service)(Service)Provider的Get瑟维斯(Service)方法来探寻BooksService(Service)(代码文件Books山姆(Sam)pleWithDI
/ 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“。*

lovebet爱博体育,创制模型  

本章的率先个示例映射单个表。第二个例证显示了创造表之间的关联。在本节中利用C#代码创立数据库而没有利用SQL
DDL语句(或透过采取设计器)成立数据库。 

示范应用程序Menus山姆ple使用以下倚重项和命名空间:

  依赖项

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属性简单定义(代码文件Menus山姆(Sam)ple /
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和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);
  }
}

使用.NET CLI进行搬迁

要利用C#代码自动创立数据库,可以应用enet工具使用package
dotnet-ef扩张.NET
CLI工具。此软件包包含用于为搬迁创设C#代码的下令。通过设置dotnet-ef
NuGet包可以使命令可用。您可以经过从体系安排文件(代码文件Menus山姆(Sam)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");
     });
  }
}

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

注意 正在进展的各种更改都得以创制另一个搬迁。新搬迁仅定义从原先版本到新本子所需的改变。假若客户的数据库需要从随机早期的本子更新,迁移数据库时调用必要的搬迁。 

在支付过程中,也行不需要所有的动迁,可能需要从品类中开创,因为可能没有该类临时气象的数据库存在。在这种情况下可以去除迁移并创办一个较大的新搬迁。

行使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类的扩展方法。

运作程序将开创表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 列的尺寸可以从NVARCHAR(MAX)减小,SQL
Server定义了可用以Price列的Money类型,并且协会名称可以从dbo更改。 Entity
Framework提供了多少个选用从代码中履行那多少个改动:数据批注和Fluent
API,下边将探讨。

数量批注

潜移默化生成的数据库的一种方法是向实体类型丰裕数据注释。可以行使Table属性更改表的名号。要改成结构名称,Table属性定义Schema属性。倘诺要为字符串类型指定不同的尺寸,可以使用马克斯(Max)Length属性(代码文件MenusWithDataAnnotations
/ MenuCard.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 和一个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上访问数据库山姆(Sam)pleDatabase。使用的提供程序是EntityFramework.MicrosoftSqlServer。这么些NuGet包以及有着同样名称和设计后缀的NuGet包必须添加到项目中。 

运行此命令后,可以见到DbContext派生类以及变更的模子类型。默认意况下,模型的布局利用fluent
API完成。可是也足以将其改变为运用提供-a选项的多寡批注。还足以影响生成的上下文类名称以及出口目录。只需接纳选拔-h检查不同的可用选项。

 

—————-未完待续