Skip to content

EF Core

概述

Entity Framework (EF) Core 是轻量级、可扩展、开源和跨平台的实体框架数据访问技术。

EF Core可以作为一个对象-关系映射器(O/RM),它使.net开发人员能够使用.net对象处理数据库, 消除了通常需要编写的大多数数据访问代码的需要。

EF Core支持多种数据库引擎,详见Database Providers

使用方法

安装

安装 NuGet 包:

  1. 核心包 Microsoft.EntityFrameworkCore

  2. 懒加载(可选) Microsoft.EntityFrameworkCore.Proxie

  3. 数据库驱动包

数据库系统配置示例NuGet 程序包
SQL Server 或 Azure SQL.UseSqlServer(connectionString)Microsoft.EntityFrameworkCore.SqlServer
Azure Cosmos DB.UseCosmos(connectionString, databaseName)Microsoft.EntityFrameworkCore.Cosmos
SQLite.UseSqlite(connectionString)Microsoft.EntityFrameworkCore.Sqlite
PostgreSQL*.UseNpgsql(connectionString)Npgsql.EntityFrameworkCore.PostgreSQL
MySQL/MariaDB*.UseMySql(connectionString)Pomelo.EntityFrameworkCore.MySql
Oracle*.UseOracle(connectionString)Oracle.EntityFrameworkCore

注意

  1. 由于微软支持问题,建议选择Pomelo.EntityFrameworkCore.MySql作为mysql驱动
  2. 相关依赖包建议选择与EntityFrameworkCore相同版本的包

图片

执行过程

通常情况下EF Core执行过程如下: 图片

DbContext

在 Entity Framework Core (EF Core) 中,DbContext 是与数据库交互的主要类。

它充当了应用程序和数据库之间的中介,负责管理对象的查询、更新、插入和删除操作。

DbContext 类通常用于定义数据库模型和配置数据库连接。

它还负责数据库的迁移、跟踪实体的状态等功能。

生命周期

DbContext 的生命周期从创建实例时开始,并在释放实例时结束。

DbContext 实例旨在用于单个工作单元。 这意味着 DbContext 实例的生存期通常很短。

注意

  1. 工作单元参考执行过程
  2. 请务必使用后释放 DbContext
  3. DbContext 不是 线程安全的。不要在线程之间共享上下文。请确保在继续使用上下文实例之前,等待所有异步调用。

在ASP.NET Core中,建议使用依赖注入管理DbContext 的生命周期。

例如:

C#
var connectionString =
    builder.Configuration.GetConnectionString("DefaultConnection")
        ?? throw new InvalidOperationException("Connection string"
        + "'DefaultConnection' not found.");

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

或者:

C#
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
    //register dbcontext
    //notice lifecycle
    //AppDomain.CurrentDomain.GetAssemblies() 只会获取到已加载到当前域的程序集。
    //可以先将所有程序集加载之后再进行读取:
    if (DependencyContext.Default != null)
    {
        DependencyContext.Default.RuntimeLibraries
           .Where(o => o.Name.StartsWith("Domain."))
           .Select(o => Assembly.Load(new AssemblyName(o.Name)))
           .ToArray();
        var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(r => r.GetName().ToString().StartsWith("Domain")).ToArray();
        if (assemblies != null && assemblies.Any())
        {
            //注册所有DbContext子类
            containerBuilder.RegisterAssemblyTypes(assemblies)
                .Where(t => typeof(DbContext).IsAssignableFrom(t) && !t.IsAbstract)
                .AsSelf()
                .InstancePerDependency();
        }
    }
});

配置

配置连接字符串
  • 依赖注入时配置:
C#
var connectionString =
    builder.Configuration.GetConnectionString("DefaultConnection")
        ?? throw new InvalidOperationException("Connection string"
        + "'DefaultConnection' not found.");

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

appsettings.json

json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}
  • 自定义的Context中重写OnConfiguring配置:
C#
public class CustomContext : DbContext
{
    private ILogger<CustomContext> _logger;
    public CustomContext(ILogger<CustomContext> logger) : base()
    {
        _logger = logger;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connectInfo = AppSetting.ConnectionsInfo.ConnectionList.FirstOrDefault(r => r.Name == "Custom");
        if (connectInfo == null)
        {
            throw new Exception("connect info not found!");
        }
        switch (connectInfo.DBType)
        {
            case "MySql":
                optionsBuilder.EnableSensitiveDataLogging().UseMySql(connectInfo.ConnectionString, new MySqlServerVersion(new Version(8, 0, 0)));
                break;
            case "SqlServer":
                optionsBuilder.EnableSensitiveDataLogging().UseSqlServer(connectInfo.ConnectionString);
                break;
            default:
                throw new Exception("un-support database!");
        }
        if(connectInfo.EnableSqlLog)
            optionsBuilder.LogTo(msg => _logger.LogInformation(msg));
    }
}

appsettings.json

json
  "ConnectionsInfo": {
    "ConnectionList": [
      {
        "Name": "Custom",
        "EnableSqlLog": "false",
        "DBType": "MySql",
        "ConnectionString": "Data Source=localhost;Database=domain_master;AllowLoadLocalInfile=true;User ID=root;Password=root;allowPublicKeyRetrieval=true;pooling=true;CharSet=utf8;port=3306;sslmode=none;"
      }
    ]
  },

数据库驱动

其中数据库驱动的示例如下:

数据库系统配置示例NuGet 程序包
SQL Server 或 Azure SQL.UseSqlServer(connectionString)Microsoft.EntityFrameworkCore.SqlServer
Azure Cosmos DB.UseCosmos(connectionString, databaseName)Microsoft.EntityFrameworkCore.Cosmos
SQLite.UseSqlite(connectionString)Microsoft.EntityFrameworkCore.Sqlite
PostgreSQL.UseNpgsql(connectionString)Npgsql.EntityFrameworkCore.PostgreSQL
MySQL/MariaDB.UseMySql(connectionString)Pomelo.EntityFrameworkCore.MySql
Oracle.UseOracle(connectionString)Oracle.EntityFrameworkCore
配置实体
  • 使用 fluent API 加载实体:
C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .IsRequired();
}
  • 批量加载实体:
C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //load entities
    var compilationLibrary = DependencyContext
        .Default?
        .RuntimeLibraries
        .Where(x => !x.Serviceable && x.Type != "package" && x.Type == "project");
    if (compilationLibrary == null)
    {
        return;
    }
    foreach (var _compilation in compilationLibrary)
    {
        AssemblyLoadContext.Default
            .LoadFromAssemblyName(new AssemblyName(_compilation.Name))
            .GetTypes()
            .Where(x =>
                x.GetTypeInfo().BaseType != null
                && x.BaseType == (typeof(CustomBaseEntity))) //CustomBaseEntity为自定义的实体基类
                .ToList().ForEach(t =>
                {
                    modelBuilder.Entity(t);
                });
    }
}
其他配置
  • 配置方法 以开启Sql日志为例:
c#
private ILogger<CustomContext> _logger;

public CustomContext(ILogger<CustomContext> logger) : base()
{
    _logger = logger;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    // 开启Sql日志
    if(connectInfo.EnableSqlLog)
        optionsBuilder.LogTo(msg => _logger.LogInformation(msg));
}
  • 常见方法
DbContextOptionsBuilder 方法作用
UseQueryTrackingBehavior设置查询的默认跟踪行为
LogTo获取 EF Core 日志的一种简单方法
UseLoggerFactory注册 Microsoft.Extensions.Logging 工厂
EnableSensitiveDataLogging在异常和日志记录中包括应用程序数据
EnableDetailedErrors更详细的查询错误(以性能为代价)
ConfigureWarnings忽略或引发警告和其他事
AddInterceptors注册 EF Core 侦听器
UseLazyLoadingProxies使用动态代理进行延迟加载

备注

UseLazyLoadingProxies 和 UseChangeTrackingProxies 是 Microsoft.EntityFrameworkCore.Proxies NuGet 包中的扩展方法。

建议的方式是使用这种类型的“.UseSomething()”调用来配置和/或使用其他包中包含的 EF Core 扩展。

初始化

  • 使用“new”进行基本 DbContext 初始化
C#
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

//使用
using var context = new ApplicationDbContext(contextOptions);
  • 使用 DbContext 工厂
c#
//Program.cs
builder.Services.AddDbContextFactory<ApplicationDbContext>(
        options => options.UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"));

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

//使用
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;

public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
    _contextFactory = contextFactory;
}

public void DoSomething()
{
    using (var context = _contextFactory.CreateDbContext())
    {
        // ...
    }
}
  • 使用依赖注入进行 DbContext 初始化 通过AddDbContext或者其他DI注入的DbContext,可以使用DI管理其生命周期。

详情参考依赖注入

上下文池

DbContext 池 是一种优化技术,用于重用已创建的 DbContext 实例,而不是每次需要一个新的实例时都重新创建它。

这样可以减少创建和销毁 DbContext 对象的开销,特别是在高负载的应用程序中,提高性能。

EF Core 的 DbContext 是 轻量级的,每个请求的 DbContext 都会被分配一个新实例,这意味着它们的创建、销毁会产生一定的性能开销。

而使用池化技术时,DbContext 会在池中进行复用,避免重复的实例化过程。

工作原理

DbContext 池使用 对象池(Object Pool)来管理 DbContext 实例。当请求一个 DbContext 时,首先检查池中是否有可用的实例。如果有,直接返回池中的实例;如果没有,则创建一个新的实例并将其添加到池中。使用过的 DbContext 实例会在使用完成后被返回到池中,而不是被销毁。

EF Core 提供了内置的对象池管理器来处理 DbContext 实例的池化过程,避免了手动管理池化对象的复杂性。

启用 DbContext 池

要启用 DbContext 池,首先需要在 ASP.NET Core 的 依赖注入(DI)容器 中进行配置。使用 AddDbContextPool 方法来启用池化。

C#
public void ConfigureServices(IServiceCollection services)
{
    // 启用 DbContext 池
    services.AddDbContextPool<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
生命周期管理

当使用 DbContext 池时,DbContext 实例的生命周期由 对象池 管理,而不是由 DI 容器直接控制。池中的实例会在请求处理完成后返回池中,而不会被销毁。

DbContext 仍然是 Scoped,这意味着它在每个 HTTP 请求期间是唯一的,池中的实例在请求结束后返回到池中,供下一次请求使用。

常见问题
  1. 确保 DbContext 的正确使用:每个请求中应该只使用一个 DbContext 实例,避免在同一个请求中复用相同的 DbContext 实例。
  2. 不要手动管理 DbContext 实例的生命周期:EF Core 管理池的生命周期,不要手动控制实例的创建和销毁。
  3. 清理状态:确保 DbContext 中的状态(如跟踪的实体)在每次请求结束后被清理干净,以避免污染池中的实例。
  4. 配置正确的池大小:如果你的应用程序有大量的数据库请求,可以增加池的大小,以提高性能。然而,池的大小也不能太大,以免占用过多内存资源。注意EF Core 会根据应用程序的负载和请求量来管理池的大小。
  5. 避免长时间持有 DbContext 实例:DbContext 的生命周期应该是短暂的,不要在多个请求中持有同一个实例。如果长时间持有实例,可能会导致内存占用过高或者状态不一致的问题。
  6. 启用数据库连接池:除了启用 DbContext 池之外,确保数据库连接本身也使用池化,这对于提高性能非常重要。大多数数据库提供程序(如 SQL Server)支持连接池,EF Core 默认启用数据库连接池。

注意

  1. 包含复杂生命周期的 DbContext,与 HTTP 请求没有紧密联系的,不推荐使用 DbContext 池。
  2. DbContext 池不能解决数据库瓶颈问题,在大多数场景下作用有限。

Model

在 Entity Framework Core (EF Core) 中,Model 配置是指配置实体类和数据库之间的映射规则。 这些配置帮助 EF Core 知道如何将 C# 类与数据库表、列以及其他数据库结构元素映射在一起。

配置方式

模型配置可以通过 数据注解(Data Annotations)和 Fluent API 两种方式进行。

  1. 数据注解示例
C#
public class Person
{
    public int PersonId { get; set; }

    [Required]  // 表示此字段是必填的
    [StringLength(100)]  // 设置字符串的最大长度
    public string Name { get; set; }

    [Column("BirthDate")]  // 映射到数据库中的 BirthDate 列
    public DateTime DateOfBirth { get; set; }

    [Range(0, 120)]  // 设置年龄字段的有效范围
    public int Age { get; set; }
}
  1. Fluent API 示例
C#
public class ApplicationDbContext : DbContext
{
    public DbSet<Person> Persons { get; set; }

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

        // 配置 Person 类
        modelBuilder.Entity<Person>()
            .ToTable("People") // 映射到 "People" 表
            .HasKey(p => p.PersonId);  // 设置主键

        modelBuilder.Entity<Person>()
            .Property(p => p.Name)
            .IsRequired() // 设置字段为必填
            .HasMaxLength(100);  // 限制最大长度为100

        modelBuilder.Entity<Person>()
            .Property(p => p.DateOfBirth)
            .HasColumnName("BirthDate");  // 映射到 "BirthDate" 列

        modelBuilder.Entity<Person>()
            .Property(p => p.Age)
            .HasRange(0, 120);  // 设置年龄的范围
    }
}

内置约定

EF Core 包括许多默认启用的模型生成约定。 可以在实现 IConvention 接口的类列表中找到所有这些约定。 但是,该列表不包括第三方数据库提供程序和插件引入的约定。

应用程序可以删除或替换这些约定中的任何一个,并添加新的自定义约定,这些约定可对 EF 未立即识别的模式应用配置。

有时,其中一个内置约定可能不适用于你的应用程序,在这种情况下,可以将其删除。

示例:

C#
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    //删除外建索引
    configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}

提示

EF Core 实体配置与数据库配置是可分离的,可以在 EF Core 中对实体类属性进行配置(如数据类型、索引、默认值等), 这些配置不必与数据库中的配置完全一致,可以在数据库和实体类之间做一定的差异化配置。

在实际开发中,通常会结合使用这两种方式,在 EF Core 中进行常规的实体映射和迁移管理,同时在数据库中进行性能调优和高级配置。

  1. 数据库配置的优势
  • 直接操作数据库:数据库配置能够直接控制数据库表结构、数据类型、索引等。您可以完全掌控数据库性能相关的配置, 比如创建复杂的索引、设置数据库级别的约束等。 这对于性能优化至关重要,特别是对于大型系统、需要高度优化的查询等场景,数据库级别的优化可以更高效地执行。
  • 高级特性支持:某些数据库特性(如分区表、视图、触发器等)可以在数据库层进行管理和配置。通过数据库配置, 可以充分利用这些特性,提高应用的性能和可维护性。
  • 更精细的控制:数据库配置允许更精细的控制,例如,使用数据库的特定数据类型(如 DATE、TIME、JSON 等)。 EF Core 虽然可以通过 Fluent API 来配置某些数据类型,但数据库直接配置能够利用数据库本身的类型和约束,避免跨平台时的限制。
  • 共享数据库配置:当有多个应用需要访问同一个数据库时,数据库的配置可以作为共享的标准,确保各个应用中的表结构、数据类型、索引等保持一致。 如果在数据库层配置了默认值、约束等规则,它们会被所有访问数据库的应用自动遵循,而不必依赖于应用代码。
  • 数据库级别的事务控制:数据库级别的事务控制(如 Isolation Level、ACID 事务等)直接影响到数据一致性和并发控制。 通过数据库配置,您可以管理事务的隔离级别、死锁避免等细节,这是在应用层无法轻易做到的。
  • 无需修改应用代码:如果数据库的配置变化不影响业务逻辑(如修改数据类型、添加索引等),那么开发者可以直接在数据库层面进行更改, 而无需修改应用代码或迁移数据库。这样可以简化开发和维护过程,尤其是在数据存储需求变动较大的情况下。
  1. EF Core 实体配置的优势
  • 应用层控制:EF Core 允许开发者在应用层面对实体类进行详细配置。这提供了比数据库配置更多的灵活性, 特别是在需要对特定应用场景进行个性化配置时(如调试环境中的数据库字段调整,或开发阶段的快速更改)。 开发者可以通过 Fluent API 或数据注解轻松改变实体类与数据库的映射,不需要直接修改数据库。
  • 跨平台支持:EF Core 是跨平台的,意味着它支持多种数据库系统(如 SQL Server、MySQL、PostgreSQL 等)。 通过在实体类中配置映射,可以避免直接操作数据库的特定平台功能,从而使得代码可以在多个数据库系统之间迁移。 即使数据库的底层实现发生了变化,应用代码的变动也会最小化。
  • 数据库独立性:通过 EF Core 的实体配置,可以隐藏数据库实现的细节,将重点放在业务逻辑上,而不需要关心具体的数据库配置。 例如,您可以在应用中统一使用 string 类型进行配置,而 EF Core 会根据不同的数据库平台将其转换为相应的数据库类型。 这样做提高了应用的数据库独立性,并降低了数据库切换的成本。
  • 数据库迁移:EF Core 提供了强大的 迁移(Migrations) 功能,帮助开发者在数据库表结构或属性更改时,自动生成 SQL 脚本并同步到数据库。 这使得数据库的变更管理变得非常简单,尤其是在团队开发中,所有的迁移都会自动化并且可以通过版本控制进行管理。
  • 代码优先(Code-First)开发:在开发初期,使用 EF Core 的 Code-First 模式可以通过代码定义实体类,进而生成数据库。 这使得数据库结构完全由代码控制,而不需要手动创建数据库表结构。 这个过程可以通过迁移脚本逐步进行,也使得开发过程更加灵活,能够更容易地与代码同步。
  • 业务逻辑的集成:通过在实体类中进行配置,您可以直接在应用层定义字段的映射、验证规则、数据格式等。 这种方式将数据库配置与应用逻辑紧密结合,可以确保业务逻辑和数据库结构在同一层面进行更新和维护。
  • 灵活的数据验证和约束:EF Core 允许在实体类中使用数据注解来进行基本的验证和约束, 例如[Required][MaxLength][Range] 等。这样,数据验证就不依赖于数据库层面的约束, 而是与应用层的逻辑一起定义。
  • 模拟和测试支持:通过 EF CoreIn-Memory Database依赖注入(DI),您可以轻松地测试实体类的行为,而不需要连接到真实的数据库。 这使得应用的单元测试变得更加容易,并且减少了测试环境的复杂性。

实体配置

配置方式参考配置实体

常见实体配置如下:

使用DbSet属性配置实体类型
C#
public class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

}
使用特性[NotMapped]从模型中排除类型
C#
[NotMapped]
public class BlogMetadata
{
    public DateTime LoadedFromDatabase { get; set; }
}

C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore<BlogMetadata>();
}
可以使用特性[Table("blogs")]指定表名
C#
[Table("blogs", Schema = "blogging")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     modelBuilder.Entity<Blog>()
        .ToTable("blogs", schema: "blogging");
}
视图配置(数据注解不支持)
C#
modelBuilder.Entity<Blog>()
    .ToView("blogsView", schema: "blogging");
对数据库表设置任意文本注释
C#
[Comment("Blogs managed on the website")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>().ToTable(
        tableBuilder => tableBuilder.HasComment("Blogs managed on the website"));
}

:::

实体属性配置

常见属性配置如下:

包含和排除的属性

根据约定,所有具有获取器和设值器的公共属性都将包含在模型中。

可以按如下所示排除特定属性:

CSharp
//数据注解
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);
}
列名

按照约定,使用关系数据库时,实体属性映射到与属性同名的表列。

如果您希望为列使用不同的名称进行配置,可以使用以下代码片段:

CSharp
//数据注解
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");
}
列数据类型

使用关系数据库时,数据库提供程序会根据属性的 .NET 类型选择数据类型。

除此之外,EFCore可以配置列以指定列的确切数据类型,比如:

CSharp
//数据注解
public class Blog
{
    public int BlogId { get; set; }

    [Column(TypeName = "varchar(200)")]
    public string Url { get; set; }

    [Column(TypeName = "decimal(5, 2)")]
    public decimal Rating { get; set; }
}

//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(
        eb =>
        {
            eb.Property(b => b.Url).HasColumnType("varchar(200)");
            eb.Property(b => b.Rating).HasColumnType("decimal(5, 2)");
        });
}
最大长度

最大长度仅适用于数组数据类型,例如 string 和 byte[]。

注意

EFCore 校验数据库校验是分离的, 即使 EF Core 没有抛出错误(例如,如果未在模型上配置最大长度),当数据被提交到数据库时, 如果插入的字符串长度超过了数据库列的最大长度, 数据库会强制执行这个约束,抛出 String or binary data would be truncated 错误。

比如: EFCore字段最大长度是100,但数据库列最大长度是50,当插入长度为80的数据时,会由数据库校验报错。

示例:

CSharp
//数据注解
public class Blog
{
    public int BlogId { get; set; }

    [MaxLength(500)]
    public string Url { get; set; }
}

//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .HasMaxLength(500);
}
精度和小数位数

decimal 属性,精度用于定义表示列将包含的任何值所需的最大位数,小数位数用于定义所需的最大小数位数。

DateTime 属性,精度用于定义表示秒的小数部分所需的最大位数,不使用小数位数。

注意

EFCore 校验数据库校验是分离的, 即使 EF Core 没有抛出错误(例如,如果未在模型上配置最大长度),当数据被提交到数据库时, 如果插入的字符串长度超过了数据库列的最大长度, 数据库会强制执行这个约束,抛出 String or binary data would be truncated 错误。

另外:SQL Server中数据类型为 datetime 的列不允许设置精度,而 datetime2 的精度介于 0 和 7 之间

示例:

CSharp
//数据注解
public class Blog
{
    public int BlogId { get; set; }
    [Precision(14, 2)]
    public decimal Score { get; set; }
    [Precision(3)]
    public DateTime LastUpdated { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Score)
        .HasPrecision(14, 2);

    modelBuilder.Entity<Blog>()
        .Property(b => b.LastUpdated)
        .HasPrecision(3);
}
Unicode

默认情况下,文本属性配置为 Unicode。 可以将列配置为非 Unicode

示例:

CSharp
//数据注解
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }

    [Unicode(false)]
    [MaxLength(22)]
    public string Isbn { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>()
        .Property(b => b.Isbn)
        .IsUnicode(false);
}
必需属性和可选属性

示例:

CSharp
//数据注解
public class Blog
{
    public int BlogId { get; set; }

    [Required]
    public string Url { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .IsRequired();
}
列注释

示例:

CSharp
//数据注解
public class Blog
{
    public int BlogId { get; set; }

    [Comment("The URL of the blog")]
    public string Url { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .HasComment("The URL of the blog");
}
列顺序

默认情况下,在使用迁移创建表时,EF Core 首先为主键列排序,然后为实体类型和从属类型的属性排序,最后为基类型中的属性排序。

也可以指定不同的列顺序。

一般情况下,大多数数据库仅支持在创建表时对列进行排序。 这意味着列顺序属性不能用于对现有表中的列重新排序。

示例:

CSharp
//数据注解
public class EntityBase
{
    [Column(Order = 0)]
    public int Id { get; set; }
}

public class PersonBase : EntityBase
{
    [Column(Order = 1)]
    public string FirstName { get; set; }

    [Column(Order = 2)]
    public string LastName { get; set; }
}

public class Employee : PersonBase
{
    public string Department { get; set; }
    public decimal AnnualSalary { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>(x =>
    {
        x.Property(b => b.Id)
            .HasColumnOrder(0);

        x.Property(b => b.FirstName)
            .HasColumnOrder(1);

        x.Property(b => b.LastName)
            .HasColumnOrder(2);
    });
}

实体键配置

Generated Values配置

查询

新增

修改

删除

原生Sql执行

事务

其他功能

生命周期

Document Base on VitePress.