设置已生成属性的显式值Setting Explicit Values for Generated Properties

生成的属性是在添加和/或更新实体时由 EF 或数据库生成其值的属性。 有关详细信息,请参阅生成的属性

在某些情况下,可能希望为已生成属性设置显式值,而不是生成一个显式值。

提示

可在 GitHub 上查看此文章的示例

模型The model

本文中使用的模型包含单个 Employee 实体。

  1. public class Employee
  2. {
  3. public int EmployeeId { get; set; }
  4. public string Name { get; set; }
  5. public DateTime EmploymentStarted { get; set; }
  6. public int Salary { get; set; }
  7. public DateTime? LastPayRaise { get; set; }
  8. }

在添加期间保存显式值Saving an explicit value during add

Employee.EmploymentStarted 属性配置为由数据库为新实体生成值(使用默认值)。

  1. modelBuilder.Entity<Employee>()
  2. .Property(b => b.EmploymentStarted)
  3. .HasDefaultValueSql("CONVERT(date, GETDATE())");

以下代码可将两个员工插入到数据库中。

  • 对于第一个员工,没有为 Employee.EmploymentStarted 属性分配任何值,因此仍将其设置为 CLR 的 DateTime 默认值。
  • 对于第二个员工,已设置显式值 1-Jan-2000
  1. using (var context = new EmployeeContext())
  2. {
  3. context.Employees.Add(new Employee { Name = "John Doe" });
  4. context.Employees.Add(new Employee { Name = "Jane Doe", EmploymentStarted = new DateTime(2000, 1, 1) });
  5. context.SaveChanges();
  6. foreach (var employee in context.Employees)
  7. {
  8. Console.WriteLine(employee.EmployeeId + ": " + employee.Name + ", " + employee.EmploymentStarted);
  9. }
  10. }

输出显示了数据库已为第一个员工生成值,且显式值已用于第二个员工。

  1. 1: John Doe, 1/26/2017 12:00:00 AM
  2. 2: Jane Doe, 1/1/2000 12:00:00 AM

显式值插入 SQL Server IDENTITY 列Explicit values into SQL Server IDENTITY columns

按照约定,Employee.EmployeeId 属性是存储生成的 IDENTITY 列。

对于大多数情况,上述方法将适用于键属性。 但是,若要将显式值插入到 SQL Server IDENTITY 列中,则必须在调用 SaveChanges() 之前手动启用 IDENTITY_INSERT

备注

对于积压工作存在一个功能请求,请求在 SQL Server 提供程序内自动执行此操作。

  1. using (var context = new EmployeeContext())
  2. {
  3. context.Employees.Add(new Employee { EmployeeId = 100, Name = "John Doe" });
  4. context.Employees.Add(new Employee { EmployeeId = 101, Name = "Jane Doe" });
  5. context.Database.OpenConnection();
  6. try
  7. {
  8. context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Employees ON");
  9. context.SaveChanges();
  10. context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Employees OFF");
  11. }
  12. finally
  13. {
  14. context.Database.CloseConnection();
  15. }
  16. foreach (var employee in context.Employees)
  17. {
  18. Console.WriteLine(employee.EmployeeId + ": " + employee.Name);
  19. }
  20. }

输出显示了提供的 ID 已保存到数据库。

  1. 100: John Doe
  2. 101: Jane Doe

在更新期间设置显式值Setting an explicit value during update

Employee.LastPayRaise 属性配置为在更新期间由数据库生成值。

  1. modelBuilder.Entity<Employee>()
  2. .Property(b => b.LastPayRaise)
  3. .ValueGeneratedOnAddOrUpdate();
  4. modelBuilder.Entity<Employee>()
  5. .Property(b => b.LastPayRaise)
  6. .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);

备注

默认情况下,如果尝试保存配置为在更新期间生成的属性的显式值,EF Core 将引发异常。 若要避免此问题,必须下拉到较低级别的元数据 API 并设置 AfterSaveBehavior(如上所示)。

备注

EF Core 2.0 中的更改: 在以前版本中,通过 IsReadOnlyAfterSave 标志控制保存后行为。 此标志已过时,将替换为 AfterSaveBehavior

数据库中还存在触发器,以便在执行 UPDATE 操作期间为 LastPayRaise 列生成值。

  1. CREATE TRIGGER [dbo].[Employees_UPDATE] ON [dbo].[Employees]
  2. AFTER UPDATE
  3. AS
  4. BEGIN
  5. SET NOCOUNT ON;
  6. IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
  7. IF UPDATE(Salary) AND NOT Update(LastPayRaise)
  8. BEGIN
  9. DECLARE @Id INT
  10. DECLARE @OldSalary INT
  11. DECLARE @NewSalary INT
  12. SELECT @Id = INSERTED.EmployeeId, @NewSalary = Salary
  13. FROM INSERTED
  14. SELECT @OldSalary = Salary
  15. FROM deleted
  16. IF @NewSalary > @OldSalary
  17. BEGIN
  18. UPDATE dbo.Employees
  19. SET LastPayRaise = CONVERT(date, GETDATE())
  20. WHERE EmployeeId = @Id
  21. END
  22. END
  23. END

以下代码可增加数据库中两个员工的薪金。

  • 对于第一个员工,没有为 Employee.LastPayRaise 属性分配任何值,因此仍将设置为 null。
  • 对于第二个员工,已将显式值设置为一周前(使加薪在较早的日期开始生效)。
  1. using (var context = new EmployeeContext())
  2. {
  3. var john = context.Employees.Single(e => e.Name == "John Doe");
  4. john.Salary = 200;
  5. var jane = context.Employees.Single(e => e.Name == "Jane Doe");
  6. jane.Salary = 200;
  7. jane.LastPayRaise = DateTime.Today.AddDays(-7);
  8. context.SaveChanges();
  9. foreach (var employee in context.Employees)
  10. {
  11. Console.WriteLine(employee.EmployeeId + ": " + employee.Name + ", " + employee.LastPayRaise);
  12. }
  13. }

输出显示了数据库已为第一个员工生成值,且显式值已用于第二个员工。

  1. 1: John Doe, 1/26/2017 12:00:00 AM
  2. 2: Jane Doe, 1/19/2017 12:00:00 AM