EF Core
微软参考文档
概述
Entity Framework (EF) Core 是轻量级、可扩展、开源和跨平台的实体框架数据访问技术。
EF Core可以作为一个对象-关系映射器(O/RM),它使.net开发人员能够使用.net对象处理数据库, 消除了通常需要编写的大多数数据访问代码的需要。
EF Core支持多种数据库引擎,详见Database Providers。
使用方法
安装
安装 NuGet 包:
核心包
Microsoft.EntityFrameworkCore
懒加载(可选)
Microsoft.EntityFrameworkCore.Proxie
数据库驱动包
数据库系统 | 配置示例 | 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 |
注意
- 由于微软支持问题,建议选择
Pomelo.EntityFrameworkCore.MySql
作为mysql驱动 - 相关依赖包建议选择与
EntityFrameworkCore
相同版本的包
执行过程
通常情况下EF Core执行过程如下:
DbContext
在 Entity Framework Core (EF Core) 中,DbContext 是与数据库交互的主要类。
它充当了应用程序和数据库之间的中介,负责管理对象的查询、更新、插入和删除操作。
DbContext 类通常用于定义数据库模型和配置数据库连接。
它还负责数据库的迁移、跟踪实体的状态等功能。
生命周期
DbContext 的生命周期从创建实例时开始,并在释放实例时结束。
DbContext 实例旨在用于单个工作单元
。 这意味着 DbContext 实例的生存期通常很短。
注意
工作单元
参考执行过程- 请务必使用后释放 DbContext
- DbContext 不是 线程安全的。不要在线程之间共享上下文。请确保在继续使用上下文实例之前,等待所有异步调用。
在ASP.NET Core中,建议使用依赖注入管理DbContext 的生命周期。
例如:
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string"
+ "'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
或者:
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();
}
}
});
配置
配置连接字符串
- 依赖注入时配置:
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string"
+ "'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
- 自定义的Context中重写OnConfiguring配置:
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
"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 加载实体:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
- 批量加载实体:
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日志为例:
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 初始化
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
//使用
using var context = new ApplicationDbContext(contextOptions);
- 使用 DbContext 工厂
//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 方法来启用池化。
public void ConfigureServices(IServiceCollection services)
{
// 启用 DbContext 池
services.AddDbContextPool<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
生命周期管理
当使用 DbContext 池时,DbContext 实例的生命周期由 对象池 管理,而不是由 DI 容器直接控制。池中的实例会在请求处理完成后返回池中,而不会被销毁。
DbContext 仍然是 Scoped,这意味着它在每个 HTTP 请求期间是唯一的,池中的实例在请求结束后返回到池中,供下一次请求使用。
常见问题
- 确保 DbContext 的正确使用:每个请求中应该只使用一个 DbContext 实例,避免在同一个请求中复用相同的 DbContext 实例。
- 不要手动管理 DbContext 实例的生命周期:EF Core 管理池的生命周期,不要手动控制实例的创建和销毁。
- 清理状态:确保 DbContext 中的状态(如跟踪的实体)在每次请求结束后被清理干净,以避免污染池中的实例。
- 配置正确的池大小:如果你的应用程序有大量的数据库请求,可以增加池的大小,以提高性能。然而,池的大小也不能太大,以免占用过多内存资源。注意EF Core 会根据应用程序的负载和请求量来管理池的大小。
- 避免长时间持有 DbContext 实例:DbContext 的生命周期应该是短暂的,不要在多个请求中持有同一个实例。如果长时间持有实例,可能会导致内存占用过高或者状态不一致的问题。
- 启用数据库连接池:除了启用 DbContext 池之外,确保数据库连接本身也使用池化,这对于提高性能非常重要。大多数数据库提供程序(如 SQL Server)支持连接池,EF Core 默认启用数据库连接池。
注意
- 包含复杂生命周期的 DbContext,与 HTTP 请求没有紧密联系的,不推荐使用 DbContext 池。
- DbContext 池不能解决数据库瓶颈问题,在大多数场景下作用有限。
Model
在 Entity Framework Core (EF Core) 中,Model 配置是指配置实体类和数据库之间的映射规则。 这些配置帮助 EF Core 知道如何将 C# 类与数据库表、列以及其他数据库结构元素映射在一起。
配置方式
模型配置可以通过 数据注解(Data Annotations)和 Fluent API 两种方式进行。
- 数据注解示例
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; }
}
- Fluent API 示例
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 未立即识别的模式应用配置。
有时,其中一个内置约定可能不适用于你的应用程序,在这种情况下,可以将其删除。
示例:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
//删除外建索引
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}
提示
EF Core 实体配置与数据库配置是可分离的,可以在 EF Core 中对实体类属性进行配置(如数据类型、索引、默认值等), 这些配置不必与数据库中的配置完全一致,可以在数据库和实体类之间做一定的差异化配置。
在实际开发中,通常会结合使用这两种方式,在 EF Core 中进行常规的实体映射和迁移管理,同时在数据库中进行性能调优和高级配置。
- 数据库配置的优势
- 直接操作数据库:数据库配置能够直接控制数据库表结构、数据类型、索引等。您可以完全掌控数据库性能相关的配置, 比如创建复杂的索引、设置数据库级别的约束等。 这对于性能优化至关重要,特别是对于大型系统、需要高度优化的查询等场景,数据库级别的优化可以更高效地执行。
- 高级特性支持:某些数据库特性(如分区表、视图、触发器等)可以在数据库层进行管理和配置。通过数据库配置, 可以充分利用这些特性,提高应用的性能和可维护性。
- 更精细的控制:数据库配置允许更精细的控制,例如,使用数据库的特定数据类型(如 DATE、TIME、JSON 等)。 EF Core 虽然可以通过 Fluent API 来配置某些数据类型,但数据库直接配置能够利用数据库本身的类型和约束,避免跨平台时的限制。
- 共享数据库配置:当有多个应用需要访问同一个数据库时,数据库的配置可以作为共享的标准,确保各个应用中的表结构、数据类型、索引等保持一致。 如果在数据库层配置了默认值、约束等规则,它们会被所有访问数据库的应用自动遵循,而不必依赖于应用代码。
- 数据库级别的事务控制:数据库级别的事务控制(如 Isolation Level、ACID 事务等)直接影响到数据一致性和并发控制。 通过数据库配置,您可以管理事务的隔离级别、死锁避免等细节,这是在应用层无法轻易做到的。
- 无需修改应用代码:如果数据库的配置变化不影响业务逻辑(如修改数据类型、添加索引等),那么开发者可以直接在数据库层面进行更改, 而无需修改应用代码或迁移数据库。这样可以简化开发和维护过程,尤其是在数据存储需求变动较大的情况下。
- 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 Core
的In-Memory Database
和依赖注入(DI)
,您可以轻松地测试实体类的行为,而不需要连接到真实的数据库。 这使得应用的单元测试变得更加容易,并且减少了测试环境的复杂性。
实体配置
配置方式参考配置实体
常见实体配置如下:
使用DbSet属性配置实体类型
public class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
使用特性[NotMapped]
从模型中排除类型
[NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
或
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<BlogMetadata>();
}
可以使用特性[Table("blogs")]
指定表名
[Table("blogs", Schema = "blogging")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
或
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("blogs", schema: "blogging");
}
视图配置(数据注解不支持)
modelBuilder.Entity<Blog>()
.ToView("blogsView", schema: "blogging");
对数据库表设置任意文本注释
[Comment("Blogs managed on the website")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
或
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().ToTable(
tableBuilder => tableBuilder.HasComment("Blogs managed on the website"));
}
:::
实体属性配置
常见属性配置如下:
包含和排除的属性
根据约定,所有具有获取器和设值器的公共属性都将包含在模型中。
可以按如下所示排除特定属性:
//数据注解
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);
}
列名
按照约定,使用关系数据库时,实体属性映射到与属性同名的表列。
如果您希望为列使用不同的名称进行配置,可以使用以下代码片段:
//数据注解
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可以配置列以指定列的确切数据类型,比如:
//数据注解
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的数据时,会由数据库校验报错。
示例:
//数据注解
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 之间
示例:
//数据注解
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
示例:
//数据注解
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);
}
必需属性和可选属性
示例:
//数据注解
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();
}
列注释
示例:
//数据注解
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 首先为主键列排序,然后为实体类型和从属类型的属性排序,最后为基类型中的属性排序。
也可以指定不同的列顺序。
一般情况下,大多数数据库仅支持在创建表时对列进行排序。 这意味着列顺序属性不能用于对现有表中的列重新排序。
示例:
//数据注解
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);
});
}