设置已生成属性的显式值Setting Explicit Values for Generated Properties
生成的属性是在添加和/或更新实体时由 EF 或数据库生成其值的属性。 有关详细信息,请参阅生成的属性。
在某些情况下,可能希望为已生成属性设置显式值,而不是生成一个显式值。
提示
可在 GitHub 上查看此文章的示例。
模型The model
本文中使用的模型包含单个 Employee
实体。
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public DateTime EmploymentStarted { get; set; }
public int Salary { get; set; }
public DateTime? LastPayRaise { get; set; }
}
在添加期间保存显式值Saving an explicit value during add
Employee.EmploymentStarted
属性配置为由数据库为新实体生成值(使用默认值)。
modelBuilder.Entity<Employee>()
.Property(b => b.EmploymentStarted)
.HasDefaultValueSql("CONVERT(date, GETDATE())");
以下代码可将两个员工插入到数据库中。
- 对于第一个员工,没有为
Employee.EmploymentStarted
属性分配任何值,因此仍将其设置为 CLR 的DateTime
默认值。 - 对于第二个员工,已设置显式值
1-Jan-2000
。
using (var context = new EmployeeContext())
{
context.Employees.Add(new Employee { Name = "John Doe" });
context.Employees.Add(new Employee { Name = "Jane Doe", EmploymentStarted = new DateTime(2000, 1, 1) });
context.SaveChanges();
foreach (var employee in context.Employees)
{
Console.WriteLine(employee.EmployeeId + ": " + employee.Name + ", " + employee.EmploymentStarted);
}
}
输出显示了数据库已为第一个员工生成值,且显式值已用于第二个员工。
1: John Doe, 1/26/2017 12:00:00 AM
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 提供程序内自动执行此操作。
using (var context = new EmployeeContext())
{
context.Employees.Add(new Employee { EmployeeId = 100, Name = "John Doe" });
context.Employees.Add(new Employee { EmployeeId = 101, Name = "Jane Doe" });
context.Database.OpenConnection();
try
{
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Employees ON");
context.SaveChanges();
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Employees OFF");
}
finally
{
context.Database.CloseConnection();
}
foreach (var employee in context.Employees)
{
Console.WriteLine(employee.EmployeeId + ": " + employee.Name);
}
}
输出显示了提供的 ID 已保存到数据库。
100: John Doe
101: Jane Doe
在更新期间设置显式值Setting an explicit value during update
Employee.LastPayRaise
属性配置为在更新期间由数据库生成值。
modelBuilder.Entity<Employee>()
.Property(b => b.LastPayRaise)
.ValueGeneratedOnAddOrUpdate();
modelBuilder.Entity<Employee>()
.Property(b => b.LastPayRaise)
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
备注
默认情况下,如果尝试保存配置为在更新期间生成的属性的显式值,EF Core 将引发异常。 若要避免此问题,必须下拉到较低级别的元数据 API 并设置 AfterSaveBehavior
(如上所示)。
备注
EF Core 2.0 中的更改: 在以前版本中,通过 IsReadOnlyAfterSave
标志控制保存后行为。 此标志已过时,将替换为 AfterSaveBehavior
。
数据库中还存在触发器,以便在执行 UPDATE
操作期间为 LastPayRaise
列生成值。
CREATE TRIGGER [dbo].[Employees_UPDATE] ON [dbo].[Employees]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
IF UPDATE(Salary) AND NOT Update(LastPayRaise)
BEGIN
DECLARE @Id INT
DECLARE @OldSalary INT
DECLARE @NewSalary INT
SELECT @Id = INSERTED.EmployeeId, @NewSalary = Salary
FROM INSERTED
SELECT @OldSalary = Salary
FROM deleted
IF @NewSalary > @OldSalary
BEGIN
UPDATE dbo.Employees
SET LastPayRaise = CONVERT(date, GETDATE())
WHERE EmployeeId = @Id
END
END
END
以下代码可增加数据库中两个员工的薪金。
- 对于第一个员工,没有为
Employee.LastPayRaise
属性分配任何值,因此仍将设置为 null。 - 对于第二个员工,已将显式值设置为一周前(使加薪在较早的日期开始生效)。
using (var context = new EmployeeContext())
{
var john = context.Employees.Single(e => e.Name == "John Doe");
john.Salary = 200;
var jane = context.Employees.Single(e => e.Name == "Jane Doe");
jane.Salary = 200;
jane.LastPayRaise = DateTime.Today.AddDays(-7);
context.SaveChanges();
foreach (var employee in context.Employees)
{
Console.WriteLine(employee.EmployeeId + ": " + employee.Name + ", " + employee.LastPayRaise);
}
}
输出显示了数据库已为第一个员工生成值,且显式值已用于第二个员工。
1: John Doe, 1/26/2017 12:00:00 AM
2: Jane Doe, 1/19/2017 12:00:00 AM