Como configurar valores explícitos para propriedades geradasSetting Explicit Values for Generated Properties

Uma propriedade gerada é uma propriedade cujo valor é gerado (EF ou banco de dados) quando a entidade é adicionada e/ou atualizada.A generated property is a property whose value is generated (either by EF or the database) when the entity is added and/or updated. Veja Propriedades geradas para saber mais.See Generated Properties for more information.

Pode haver situações em que você deseja definir um valor explícito para uma propriedade gerada, em vez de ter uma gerada.There may be situations where you want to set an explicit value for a generated property, rather than having one generated.

Dica

Veja o exemplo deste artigo no GitHub.You can view this article's sample on GitHub.

O modeloThe model

O modelo usado neste artigo contém uma única entidade Employee.The model used in this article contains a single Employee entity.

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; }
}

Salvar um valor explícito durante adicionarSaving an explicit value during add

A propriedade Employee.EmploymentStarted está configurada para ter valores gerados pelo banco de dados para novas entidades (usando um valor padrão).The Employee.EmploymentStarted property is configured to have values generated by the database for new entities (using a default value).

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

O código a seguir insere dois funcionários no banco de dados.The following code inserts two employees into the database.

  • Para a primeira, nenhum valor é atribuído à propriedade Employee.EmploymentStarted; portanto, ele permanece definido como o valor padrão CLR para DateTime.For the first, no value is assigned to Employee.EmploymentStarted property, so it remains set to the CLR default value for DateTime.
  • Para o segundo, configuramos um valor explícito de 1-Jan-2000.For the second, we have set an explicit value of 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);
    }
}

A saída mostra que o banco de dados gerou um valor para o primeiro funcionário e nosso valor explícito foi usado para o segundo.Output shows that the database generated a value for the first employee and our explicit value was used for the second.

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

Valores explícitos em colunas de IDENTITY do SQL ServerExplicit values into SQL Server IDENTITY columns

Por convenção a propriedade Employee.EmployeeId é uma coluna IDENTITY gerada pelo repositório.By convention the Employee.EmployeeId property is a store generated IDENTITY column.

Na maioria das situações, a abordagem mostrada acima funcionará para as propriedades chave.For most situations, the approach shown above will work for key properties. No entanto, para inserir valores explícitos em uma coluna IDENTITY do SQL Server, é necessário habilitar IDENTITY_INSERT manualmente antes de chamar SaveChanges().However, to insert explicit values into a SQL Server IDENTITY column, you need to manually enable IDENTITY_INSERT before calling SaveChanges().

Observação

Temos uma solicitação de recurso em nossa lista de pendências para fazer isso automaticamente dentro do provedor do SQL Server.We have a feature request on our backlog to do this automatically within the SQL Server provider.

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.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees ON");
        context.SaveChanges();
        context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees OFF");
    }
    finally
    {
        context.Database.CloseConnection();
    }


    foreach (var employee in context.Employees)
    {
        Console.WriteLine(employee.EmployeeId + ": " + employee.Name);
    }
}

A saída mostra que as ids fornecidas foram salvas no banco de dados.Output shows that the supplied ids were saved to the database.

100: John Doe
101: Jane Doe

Configurar um valor explícito durante a atualizaçãoSetting an explicit value during update

A propriedade Employee.LastPayRaise está configurada para ter valores gerados pelo banco de dados durante as atualizações.The Employee.LastPayRaise property is configured to have values generated by the database during updates.

modelBuilder.Entity<Employee>()
    .Property(b => b.LastPayRaise)
    .ValueGeneratedOnAddOrUpdate();

modelBuilder.Entity<Employee>()
    .Property(b => b.LastPayRaise)
    .Metadata.AfterSaveBehavior = PropertySaveBehavior.Ignore;

Observação

Por padrão, o EF Core gerará uma exceção se você tentar salvar um valor explícito para uma propriedade que está configurada para ser gerada durante a atualização.By default, EF Core will throw an exception if you try to save an explicit value for a property that is configured to be generated during update. Para evitar isso, você precisará passar para a API de metadados de nível inferior e definir o AfterSaveBehavior (conforme mostrado acima).To avoid this, you need to drop down to the lower level metadata API and set the AfterSaveBehavior (as shown above).

Observação

Alterações no EF Core 2.0: nas versões anteriores, o comportamento de salvar depois foi controlado por meio do sinalizador IsReadOnlyAfterSave.Changes in EF Core 2.0: In previous releases the after-save behavior was controlled through the IsReadOnlyAfterSave flag. Esse sinalizador foi obsoleto e substituído por AfterSaveBehavior.This flag has been obsoleted and replaced by AfterSaveBehavior.

Também há um gatilho no banco de dados para gerar valores para a coluna LastPayRaise durante operações UPDATE.There is also a trigger in the database to generate values for the LastPayRaise column during UPDATE operations.

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

O código a seguir aumenta o salário de dois funcionários no banco de dados.The following code increases the salary of two employees in the database.

  • Para a primeira, nenhum valor é atribuído à propriedade Employee.LastPayRaise; portanto, ele permanece definido como nulo.For the first, no value is assigned to Employee.LastPayRaise property, so it remains set to null.
  • Para o segundo, configuramos um valor explícito de uma semana atrás (antes da data do aumento de salário).For the second, we have set an explicit value of one week ago (back dating the pay raise).
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);
    }
}

A saída mostra que o banco de dados gerou um valor para o primeiro funcionário e nosso valor explícito foi usado para o segundo.Output shows that the database generated a value for the first employee and our explicit value was used for the second.

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