生成的值Generated Values

值生成模式Value generation patterns

有三个可用于属性的值生成模式:

  • 无值生成
  • 在添加时生成值
  • 在添加或更新时生成值

无值生成No value generation

没有值生成意味着,需始终提供要保存到数据库的有效值。 必须先将有效的值赋予新的实体,再将这些新的实体添加到上下文中。

在添加时生成值Value generated on add

在添加时生成值,意思是为新实体生成值。

根据所用数据库提供程序的不同,值可能会通过 EF 在客户端生成或者由数据库生成。 如果由数据库生成值,则当你将实体添加到上下文时,EF 可能会赋予一个临时值。 在 SaveChanges()期间,此临时值将替换为数据库生成的值。

如果将一个实体添加到已经为属性赋予值的上下文,则 EF 会尝试插入该值而不是生成新值。 如果某个属性未分配 CLR 默认值(null 用于 string0 用于 intGuid.Empty Guid等),则该属性被视为已分配值。 有关详细信息,请参阅生成的属性的显式值

警告

如何为添加的实体生成值取决于所用数据库提供程序。 数据库提供程序可能会为某些属性类型自动设置值的生成,但其他的属性类型可能要求你手动设置值的生成方式。

例如,使用 SQL Server 时,将自动为 GUID 属性(使用 SQL Server 顺序 GUID 算法)生成值。 但是,如果您指定在添加时生成 DateTime 属性,则必须设置一个方法来生成值。 执行此操作的一种方法是配置默认值 GETDATE(),请参阅默认值

在添加或更新时生成值Value generated on add or update

在添加或更新时生成值,意味着在每次保存该记录(插入或更新)时生成新值。

value generated on add一样,如果为新添加的实体实例的属性指定值,则将插入该值,而不是要生成的值。 还可以在更新时设置显式值。 有关详细信息,请参阅生成的属性的显式值

警告

如何在添加和更新实体时生成值取决于所用数据库提供程序。 数据库提供程序可能会为某些属性类型自动设置值的生成,但其他的属性类型会要求你手动设置值的生成方式。

例如,使用 SQL Server 时,将使用 rowversion 数据类型设置在添加或更新时生成的 byte[] 属性,并将其标记为并发标记,以便在数据库中生成值。 但是,如果您指定在添加或更新时生成 DateTime 属性,则必须设置一个方法来生成值。 实现此目的的一种方法是将默认值 GETDATE() (请参阅默认值)配置为为新行生成值。 然后即可使用数据库触发器在更新过程中生成值(如下面的示例触发器所示)。

  1. CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
  2. AFTER UPDATE
  3. AS
  4. BEGIN
  5. SET NOCOUNT ON;
  6. IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
  7. DECLARE @Id INT
  8. SELECT @Id = INSERTED.BlogId
  9. FROM INSERTED
  10. UPDATE dbo.Blogs
  11. SET LastUpdated = GETDATE()
  12. WHERE BlogId = @Id
  13. END

在添加时生成值Value generated on add

按照约定,如果应用程序不提供值,则将 “short”、”int”、”long” 或 “Guid” 类型的非复合主键设置为为插入的实体生成值。 您的数据库提供程序通常会负责必要的配置;例如,SQL Server 中的数字主键将自动设置为标识列。

可以将任何属性配置为为插入的实体生成其值,如下所示:

  1. public class Blog
  2. {
  3. public int BlogId { get; set; }
  4. public string Url { get; set; }
  5. [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  6. public DateTime Inserted { get; set; }
  7. }
  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Blog>()
  4. .Property(b => b.Inserted)
  5. .ValueGeneratedOnAdd();
  6. }

警告

这只是让 EF 知道为已添加的实体生成值,并不保证 EF 会设置实际机制来生成值。 有关更多详细信息,请参阅add 部分生成的值

默认值Default values

在关系数据库中,可以使用默认值来配置列;如果插入的行没有该列的值,将使用默认值。

可以在属性上配置默认值:

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Blog>()
  4. .Property(b => b.Rating)
  5. .HasDefaultValue(3);
  6. }

还可以指定用于计算默认值的 SQL 片段:

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Blog>()
  4. .Property(b => b.Created)
  5. .HasDefaultValueSql("getdate()");
  6. }

指定默认值会隐式将属性配置为在添加时生成的值。

在添加或更新时生成值Value generated on add or update

  1. public class Blog
  2. {
  3. public int BlogId { get; set; }
  4. public string Url { get; set; }
  5. [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
  6. public DateTime LastUpdated { get; set; }
  7. }
  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Blog>()
  4. .Property(b => b.LastUpdated)
  5. .ValueGeneratedOnAddOrUpdate();
  6. }

警告

这只是让 EF 知道为添加或更新的实体生成值,并不保证 EF 会设置实际机制来生成值。 有关更多详细信息,请参阅add or update 部分上生成的值

计算列Computed columns

在某些关系数据库上,列可以配置为在数据库中计算其值,通常具有引用其他列的表达式:

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Person>()
  4. .Property(p => p.DisplayName)
  5. .HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
  6. }

备注

在某些情况下,每次提取列的值(有时称为虚拟列)时,都将计算该列的值,而在其他情况下,会在每次更新行时计算该列的值并存储(有时称为存储列或持久化列)。 这不同于数据库提供程序。

无值生成No value generation

如果对属性的值生成进行了配置,则通常需要对其进行值生成。 例如,如果你有一个 int 类型的主键,则它将被隐式设置配置为在 add 时生成的值;可以通过以下方式禁用此操作:

  1. public class Blog
  2. {
  3. [DatabaseGenerated(DatabaseGeneratedOption.None)]
  4. public int BlogId { get; set; }
  5. public string Url { get; set; }
  6. }
  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Blog>()
  4. .Property(b => b.BlogId)
  5. .ValueGeneratedNever();
  6. }