# 模块二 基础巩固 EF Core 基础与配置

## 2.4.3 EF Core -- 基础与配置 <a href="#id-243efcore-ji-chu-yu-pei-zhi" id="id-243efcore-ji-chu-yu-pei-zhi"></a>

* 连接字符串
* 异步编程
* 日志
* DbContext池
* 类和配置表
* 属性和列配置
* 并发token
* 索引

Entity Framework Core：<https://docs.microsoft.com/zh-cn/ef/core/>

### 连接字符串 <a href="#lian-jie-zi-fu-chuan" id="lian-jie-zi-fu-chuan"></a>

连接字符串：<https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/connection-strings>

在 ASP.NET Core 配置系统非常灵活，并且可以将连接字符串存储在 appsettings.json 、环境变量、用户密钥存储或其他配置源中

appsettings.json

```
{
  "ConnectionStrings": {
    "BloggingDatabase": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
  },
}
```

### 异步编程 <a href="#yi-bu-bian-cheng" id="yi-bu-bian-cheng"></a>

异步编程：<https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/async>

当在数据库中执行查询时，异步操作将避免阻止线程。 异步操作对于在丰富的客户端应用程序中保持响应式 UI 非常重要，并且还可以增加 web 应用程序中的吞吐量，在这些应用程序中，它们可释放线程以处理 web 应用程序中的其他请求

```
var blog = new Blog { Url = "http://sample.com" };
context.Blogs.Add(blog);
await context.SaveChangesAsync();
```

### 日志 <a href="#ri-zhi" id="ri-zhi"></a>

日志：<https://docs.microsoft.com/zh-cn/ef/core/logging-events-diagnostics/extensions-logging?tabs=v3>

Entity Framework Core (EF Core) 与完全集成 Microsoft.Extensions.Logging

appsettings.json

```
"Microsoft.EntityFrameworkCore.Database.Command": "Debug"
```

启动程序，查询列表，控制台输出

```
dbug: Microsoft.EntityFrameworkCore.Database.Command[20103]
      Creating DbCommand for 'ExecuteReader'.
dbug: Microsoft.EntityFrameworkCore.Database.Command[20104]
      Created DbCommand for 'ExecuteReader' (10ms).
dbug: Microsoft.EntityFrameworkCore.Database.Command[20100]
      Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT `p`.`Id`, `p`.`CreatedAt`, `p`.`CreatedBy`, `p`.`EndDate`, `p`.`IdentityId`, `p`.`LastUpdateAt`, `p`.`LastUpdateBy`, `p`.`PlanId`, `p`.`StartDate`, `p`.`SupervisorId`, `p`.`TenantId`, `p`.`Title`, `p`.`UserId`
      FROM `Projects` AS `p`
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (82ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT `p`.`Id`, `p`.`CreatedAt`, `p`.`CreatedBy`, `p`.`EndDate`, `p`.`IdentityId`, `p`.`LastUpdateAt`, `p`.`LastUpdateBy`, `p`.`PlanId`, `p`.`StartDate`, `p`.`SupervisorId`, `p`.`TenantId`, `p`.`Title`, `p`.`UserId`
      FROM `Projects` AS `p`
dbug: Microsoft.EntityFrameworkCore.Database.Command[20300]
      A data reader was disposed.
```

### DbContext池 <a href="#dbcontext-chi" id="dbcontext-chi"></a>

DbContext池：<https://docs.microsoft.com/zh-cn/ef/core/performance/advanced-performance-topics?tabs=with-constant#dbcontext-pooling>

AddDbContextPool 启用实例的池 DbContext 。 上下文池可以通过重复使用上下文实例，而不是为每个请求创建新实例，从而提高大规模方案（如 web 服务器）的吞吐量。

```
services.AddDbContextPool<LighterDbContext>(options =>
{
    options.UseMySql(Configuration.GetConnectionString("LighterDbContext"));
});
```

AddDbContextPool使用时，在请求上下文实例时，EF 首先检查池中是否有可用的实例。 请求处理完成后，实例的任何状态都将被重置，并且实例本身会返回池中。

避免在维护状态的应用程序中使用上下文池。 例如，不应在请求之间共享的上下文中的私有字段。 在将上下文实例添加到池中之前，EF Core 仅重置它知道的状态。

除高度优化的方案外，池的性能提升通常可以忽略不计。

### 类和配置表 <a href="#lei-he-pei-zhi-biao" id="lei-he-pei-zhi-biao"></a>

实体类型：<https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=data-annotations>

在模型中包含类型

```
class MyContext : DbContext
{
    // 对应一张表（推荐）
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 对应一张表
        modelBuilder.Entity<AuditEntry>();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

public class AuditEntry
{
    public int AuditEntryId { get; set; }
    public string Username { get; set; }
    public string Action { get; set; }
}
```

从模型中排除类型

```
[NotMapped]
public class BlogMetadata
{
    public DateTime LoadedFromDatabase { get; set; }
}
```

从迁移中排除

```
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}
```

### 属性和列配置 <a href="#shu-xing-he-lie-pei-zhi" id="shu-xing-he-lie-pei-zhi"></a>

* 包含和排除的属性
* 列名
* 键
* 自动生成列

#### 包含和排除的属性 <a href="#bao-han-he-pai-chu-de-shu-xing" id="bao-han-he-pai-chu-de-shu-xing"></a>

实体属性：<https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-properties?tabs=data-annotations>

数据批注

```
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    [NotMapped]
    public DateTime LoadedFromDatabase { get; set; }
}
```

Fluent API

```
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Ignore(b => b.LoadedFromDatabase);
}
```

#### 列名 <a href="#lie-ming" id="lie-ming"></a>

数据批注

```
public class Blog
{
    [Column("blog_id")]
    public int BlogId { get; set; }
    public string Url { get; set; }
}
```

Fluent API （推荐）

```
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.BlogId)
        .HasColumnName("blog_id");
}
```

#### 键 <a href="#jian" id="jian"></a>

键：<https://docs.microsoft.com/zh-cn/ef/core/modeling/keys?tabs=data-annotations>

按照约定，将名为 Id 或的属性 Id 配置为实体的主键。

```
class Car
{
    public string Id { get; set; }

    public string Make { get; set; }
    public string Model { get; set; }
}

class Truck
{
    public string TruckId { get; set; }

    public string Make { get; set; }
    public string Model { get; set; }
}
```

可以将单个属性配置为实体的主键

数据批注

```
class Car
{
    [Key]
    public string LicensePlate { get; set; }

    public string Make { get; set; }
    public string Model { get; set; }
}
```

Fluent API

```
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Car>()
        .HasKey(c => c.LicensePlate);
}
```

#### 自动生成列 <a href="#zi-dong-sheng-cheng-lie" id="zi-dong-sheng-cheng-lie"></a>

生成的值：<https://docs.microsoft.com/zh-cn/ef/core/modeling/generated-properties?tabs=data-annotations>

通过 Fluent API 的方式添加自增列

LighterDbContext

```
modelBuilder.Entity<Project.Project>().Property(p => p.Id).ValueGeneratedOnAdd();
```

注释控制器中 Id 的赋值

ProjectController

```
//project.Id = Guid.NewGuid().ToString();
```

新增一条数据，返回 Id 是自动生成的

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FBO9bO9Dj0e1ijlYo2Isb%2F200.jpg?alt=media\&token=7c94164c-ed60-440b-9c8d-8393cfb9f1a9)

通过数据批注方式添加创建时间，修改时间默认值

Entity

```
/// <summary>
/// 创建时间
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedAt { get; set; }

/// <summary>
/// 最后修改时间
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime LastUpdateAt { get; set; }
```

更新数据库

```
dotnet ef migrations add ChangeLastUpdateByToString

dotnet ef database update
```

启动程序，新增一条数据，可以看到创建时间，修改时间默认值

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FLt9yP7ZiIaCIt1axsbg3%2F201.jpg?alt=media\&token=fb8a1abe-ea18-46cf-9835-281a21f2b2d1)

### 并发token <a href="#bing-fa-token" id="bing-fa-token"></a>

并发标记：<https://docs.microsoft.com/zh-cn/ef/core/modeling/concurrency?tabs=data-annotations>

配置为并发标记的属性用于实现乐观并发控制。

数据批注

```
public class Person
{
    public int PersonId { get; set; }

    [ConcurrencyCheck]
    public string LastName { get; set; }

    public string FirstName { get; set; }
}
```

Fluent API

```
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .Property(p => p.LastName)
        .IsConcurrencyToken();
}
```

#### Timestamp/rowversion （推荐） <a href="#timestamprowversion-tui-jian" id="timestamprowversion-tui-jian"></a>

Timestamp/rowversion 是一个属性，在每次插入或更新行时，数据库会自动为其生成新值。 此属性也被视为并发标记，这确保了在你查询行后，如果正在更新的行发生了更改，则会出现异常。

数据批注

```
public class Blog
{
    public int BlogId { get; set; }

    public string Url { get; set; }

    [Timestamp]
    public byte[] Timestamp { get; set; }
}
```

Fluent API

```
class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(p => p.Timestamp)
            .IsRowVersion();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public byte[] Timestamp { get; set; }
}
```

### 索引 <a href="#suo-yin" id="suo-yin"></a>

索引：<https://docs.microsoft.com/zh-cn/ef/core/modeling/indexes>

```
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasIndex(b => b.Url);
}
```

为多个列指定索引

```
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasIndex(p => new { p.FirstName, p.LastName });
}
```

### GitHub源码链接： <a href="#github-yuan-ma-lian-jie" id="github-yuan-ma-lian-jie"></a>

<https://github.com/MingsonZheng/ArchitectTrainingCamp/tree/main/LighterApi>
