实体属性
模型中的每个实体类型都有一组属性,EF Core 将从数据库中读取和写入这些属性。 如果使用的是关系数据库,实体属性将映射到表列。
按照约定,所有具有 Getter 和 Setter 的公共属性都将包含在模型中。
可以按如下所示排除特定属性:
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
配置为非 unicode 字符串,其最大长度为 200
,并将 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[]
。
备注
在向提供程序传递数据之前,实体框架不会执行任何最大长度的验证。 而是由提供程序或数据存储根据情况进行验证。 例如,当面向 SQL Server 时,超过最大长度将导致异常,因为基础列的数据类型不允许存储多余的数据。
在下面的示例中,将最大长度配置为 500 将导致在 SQL Server 上创建 nvarchar(500)
类型的列:
public class Blog
{
public int BlogId { get; set; }
[MaxLength(500)]
public string Url { get; set; }
}
某些关系数据类型支持精度和小数位数 Facet,它们用于控制可以存储哪些值,以及列需要多少存储。 哪些数据类型支持精度和小数位数取决于数据库,但在大多数数据库中,decimal
和 DateTime
类型支持这些 Facet。 对于 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 文本数据。 例如,在 SQL Server 中,nvarchar(x)
用于表示 UTF-16 格式的 Unicode 数据,而 varchar(x)
用于表示非 Unicode 数据(但请参阅 SQL Server UTF-8 支持中的说明)。 对于不支持此概念的数据库,配置此概念将不起作用。
默认情况下,文本属性配置为 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
不是要分配给属性的有效值,则它被视为必需属性。 映射到关系数据库架构时,必需属性创建为不可为 null 的列,可选属性创建为可为 null 的列。
按照约定,其 .NET 类型可包含 null 的属性将配置为可选属性,而 .NET 类型不能包含 null 的属性将配置为必需属性。 例如,所有具有 .NET 值类型(int
、decimal
、bool
等)的属性均配置为必需属性,所有具有可为空的 .NET 值类型(int?
、decimal?
、bool?
等)的属性均配置为可选属性。
C# 8 引入了一项名为可为 null 引用类型 (NRT) 的新功能,该功能允许对引用类型进行批注,指示引用类型能否包含 null。 默认情况下,此功能会在新项目模板中启用,但在现有项目中保持禁用,除非显式选择启用。 可为空的引用类型对 EF Core 的行为有以下影响:
- 如果禁用了可为空的引用类型,则具有 .NET 引用类型的所有属性都按约定配置为可选属性(例如
string
)。 - 如果启用了可为 null 的引用类型,则基于属性的 .NET 类型的 C# 为 Null 性来配置属性:
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 可用于替代使用特性进行的排序,包括在不同属性上的特性指定相同顺序号时解决所有冲突。
请注意,在一般情况下,大多数数据库仅支持在创建表时对列进行排序。 这意味着不能使用列顺序特性对现有表中的列进行重新排序。