实体属性
模型中的每个实体类型都有一组属性,EF Core 将从数据库读取和写入这些属性。 如果使用关系数据库,实体属性将映射到表列。
包含和排除的属性
根据 约定,所有具有获取器和设值器的公共属性都将包含在模型中。
可以按如下所示排除特定属性:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[NotMapped]
public DateTime LoadedFromDatabase { get; set; }
}
列名
按照约定,使用关系数据库时,实体属性映射到与属性同名的表列。
如果您希望为列使用不同的名称进行配置,可以使用以下代码片段:
public class Blog
{
[Column("blog_id")]
public int BlogId { get; set; }
public string Url { get; set; }
}
列数据类型
使用关系数据库时,数据库提供程序会根据属性的 .NET 类型选择数据类型。 它还考虑了其他元数据,例如配置的 最大长度、属性是否是主键的一部分等。
例如,SQL Server 提供程序将 DateTime
属性映射到 datetime2(7)
列,将 string
属性映射到 nvarchar(max)
列(若属性用作键,则映射到 nvarchar(450)
)。
还可以配置列以指定列的确切数据类型。 例如,以下代码将 Url
配置为最大长度为 200
的非 unicode 字符串,并将 Rating
配置为十进制,精度为 5
和小数位数 2
:
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; }
}
最大长度
配置最大长度为数据库提供程序提供有关为给定属性选择的相应列数据类型的提示。 最大长度仅适用于数组数据类型,例如 string
和 byte[]
。
说明
在将数据传递到提供程序之前,Entity Framework 不会对最大长度进行任何验证。 由提供程序或数据存储进行验证(如果适用)。 例如,当面向 SQL Server 时,超出最大长度将导致异常,因为基础列的数据类型不允许存储多余的数据。
在以下示例中,配置最大长度为 500 将导致在 SQL Server 上创建类型为 nvarchar(500)
的列:
public class Blog
{
public int BlogId { get; set; }
[MaxLength(500)]
public string Url { get; set; }
}
精度和小数位数
某些关系数据类型支持精度和缩放方面;这些控件控制可以存储哪些值,以及列所需的存储量。 哪些数据类型支持精度和缩放依赖于数据库,但在大多数数据库中,decimal
和 DateTime
类型确实支持这些方面。 对于 decimal
属性,精度用于定义表示列将包含的任何值所需的最大位数,小数位数用于定义所需的最大小数位数。 对于 DateTime
属性,精度用于定义表示秒的小数部分所需的最大位数,不使用小数位数。
说明
在向提供程序传递数据之前,实体框架不会执行任何精度或小数位数的验证。 应由提供程序或数据存储根据需要进行验证。 例如,当面向 SQL Server 时,数据类型为 datetime
的列不允许设置精度,而 datetime2
的精度可以介于 0 和 7 之间(含这两个值)。
在以下示例中,将 Score
属性配置为精度为 14,小数位数 2 将导致在 SQL Server 上创建类型 decimal(14,2)
列,并将 LastUpdated
属性配置为精度 3 将导致类型 datetime2(3)
列:
public class Blog
{
public int BlogId { get; set; }
[Precision(14, 2)]
public decimal Score { get; set; }
[Precision(3)]
public DateTime LastUpdated { get; set; }
}
如果不先定义精度,则永远不会定义小数位数,因此用于定义小数位数的数据注释为 [Precision(precision, scale)]
。
Unicode
在某些关系数据库中,存在不同的类型来表示 Unicode 和非 Unicode 文本数据。 例如,在 SQL Server 中,
默认情况下,文本属性配置为 Unicode。 可以将列配置为非 Unicode,如下所示:
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
[Unicode(false)]
[MaxLength(22)]
public string Isbn { get; set; }
}
必需属性和可选属性
一个属性如果能有效地包含 null
,则被认为是可选的。 如果 null
不是要分配给属性的有效值,则将其视为必需属性。 映射到关系数据库架构时,必需的属性将创建为非空列,而可选属性将创建为可空列。
惯例
按照约定,其 .NET 类型可以包含 null 的属性将被配置为可选,而 .NET 类型不能包含 null 的属性将配置为必需。 例如,所有具有 .NET 值类型的属性(int
、decimal
、bool
等)都配置为必需,并且所有具有可为 null 的 .NET 值类型(int?
、decimal?
、bool?
等)的属性都配置为可选。
C# 8 引入了一项名为可为 null 引用类型 (NRT) 的新功能,该功能允许对引用类型进行批注,指示引用类型能否包含 null。 默认情况下,此功能在新项目模板中处于启用状态,但在现有项目中保持禁用状态,除非显式选择启用此功能。 可为空的引用类型对 EF Core 的行为有以下影响:
- 如果禁用可为 null 的引用类型,则具有 .NET 引用类型的所有属性都按约定配置为可选(例如,
string
)。 - 如果启用可空引用类型,那么会根据其 .NET 类型的 C# 的可空性来配置属性:
string?
将被配置为可选,而string
将被配置为必填。
以下示例演示一个具有必需和可选属性的实体类型,分别说明了可为空的引用功能在禁用时和启用时的情况:
public class CustomerWithoutNullableReferenceTypes
{
public int Id { get; set; }
[Required] // Data annotations needed to configure as required
public string FirstName { get; set; }
[Required] // Data annotations needed to configure as required
public string LastName { get; set; }
public string MiddleName { get; set; } // Optional by convention
}
建议使用可为 null 的引用类型,因为这样可以将 C# 代码中表示的可为 null 性直接流入 EF Core 的模型和数据库,从而避免使用 Fluent API 或数据注释来重复表达同一概念。
说明
在现有项目中启用可为 null 的引用类型时,请务必小心:以前配置为可选的引用类型的属性现在会被配置为必需,除非它们被显式标记为可为 null。 管理关系数据库架构时,这可能会导致生成迁移,从而改变数据库列的为 Null 性。
若要详细了解可为 null 的引用类型以及如何将其与 EF Core 配合使用,请参阅此功能的专用文档页。
显式配置
可以按约定将可选的属性配置为必需,如下所示:
public class Blog
{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}
列排序规则
可以定义文本列的排序规则,以确定如何比较和排序。 例如,以下代码片段将 SQL Server 列配置为不区分大小写:
modelBuilder.Entity<Customer>().Property(c => c.Name)
.UseCollation("SQL_Latin1_General_CP1_CI_AS");
如果数据库中的所有列都需要使用特定的排序规则,请改为在数据库级别定义排序规则。
有关 EF Core 对排序规则的支持的常规信息,请参阅排序规则文档页。
列注释
可以为数据库列设置任意文本注释,以便在数据库中记录和说明您的架构。
public class Blog
{
public int BlogId { get; set; }
[Comment("The URL of the blog")]
public string Url { get; set; }
}
列顺序
默认情况下,在使用迁移创建表时,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 可用于替代使用特性进行的排序,包括在不同属性上的特性指定相同顺序号时解决所有冲突。
请注意,一般情况下,大多数数据库仅支持在创建表时对列进行排序。 这意味着列顺序属性不能用于对现有表中的列重新排序。