Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core 7
Если при добавлении или обновлении нового объекта у него уже установлено значение для свойства, Entity Framework использует это значение при вставке или обновлении в таблицу.
Если для свойства явным образом не установлено значение, то для свойства устанавливается значение по умолчанию (null
для string
, 0
для int
, Guid.Empty
для Guid
и т.д.).
В зависимости от используемого провайдера базы данных, значения для свойств могут генерироваться на стороне клиента с помощью EF, либо же генерироваться
уже на стороне базы данных при добавлении. Если значение генерируется базой данных, тогда при добавлении объекта в контекст EF может назначить временное
значение. Это временное значение будет заменено значением, сгенерированным базой данных при вызове метода SaveChanges()
.
По умолчанию для свойств первичных ключей, которые представляют типы int
или GUID
и которые имеют значение по умолчанию,
генерируется значение при вставке в базу данных. Для всех остальных свойств значения по умолчанию не генерируется.
Например, пусть у нас имеет следующая модель:
public class User { public int Id { get; set; } public string Name { get; set; } } public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); } }
То после добавления в базу данных мы сможем получить сгенерированный Id:
using (ApplicationContext db = new ApplicationContext()) { User user = new User { Name = "Tom" }; Console.WriteLine($"Id перед добавлением в контекст {user.Id}"); // Id = 0 db.Users.Add(user); db.SaveChanges(); Console.WriteLine($"Id после добавления в базу данных {user.Id}"); // Id = 3 }
Атрибут DatabaseGeneratedAttribute преставляет аннотацию, которая позволяет изменить поведение базы данных при добавлении или изменении.
Например, мы хотим отключить автогенерацию значения при добавлении:
using System.ComponentModel.DataAnnotations.Schema; public class User { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int Id { get; set; } public string Name { get; set; } }
И если теперь мы попробуем добавить объект без установленного Id, то EF в качесте временного значения будет использовать значение по умолчанию, то есть Id=0. В итоге при добавление более одного объекта в бд мы получим ошибку:
using (ApplicationContext db = new ApplicationContext()) { db.Users.Add(new User { Name = "Tom" }); db.Users.Add(new User { Name = "Alice" }); // Ошибка db.SaveChanges(); var users = db.Users.ToList(); foreach (var user in users) Console.WriteLine($"{user.Id} - {user.Name}"); }
В этом случае нам надо будет устанавливать Id:
using (ApplicationContext db = new ApplicationContext()) { db.Users.Add(new User { Id = 11, Name = "Tom" }); db.Users.Add(new User { Id = 23, Name = "Alice" }); db.SaveChanges(); var users = db.Users.ToList(); foreach (var user in users) Console.WriteLine($"{user.Id} - {user.Name}"); }
Если мы хотим, чтобы база данных, наоборот, сама генерировала значение, то в атрибут надо передавать значение DatabaseGeneratedOption.Identity:
public class User { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } public string Name { get; set; } }
Но в данном случае для свойства Id это значение избыточно, так как значение генерируется по умолчанию.
Отключение автогенерации значения для свойства:
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>().Property(b => b.Id).ValueGeneratedNever(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); } } public class User { public int Id { get; set; } public string Name { get; set; } }
Для свойств, которые не представляют ключи и для которых не устанавливается значения, используются значения по умолчанию. Например, для свойств типа
int
это значение 0. С помощью метода HasDefaultValue() можно переопределить значение по умолчанию, которое будет применяться
после добавления объекта в базу данных:
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>().Property(u => u.Age).HasDefaultValue(18); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); } } public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
В этом случае, если мы не укажем значение для свойства Age, то ему будет присвоено значение 18:
using (ApplicationContext db = new ApplicationContext()) { User user1 = new User() { Name = "Tom"}; Console.WriteLine($"Age: {user1.Age}"); // 0 db.Users.Add(user1); db.SaveChanges(); Console.WriteLine($"Age: {user1.Age}"); // 18 }
На уровне базы данных это будет проявляться в установке параметра DEFAULT:
CREATE TABLE [dbo].[Users] ( [Id] INT IDENTITY (1, 1) NOT NULL, [Age] INT DEFAULT (18) NOT NULL, [Name] NVARCHAR (MAX) NULL, CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([Id] ASC) );
Метод HasDefaultValueSql() также определяет генерацию значения по умолчанию, только само значение устанавливается на основе кода SQL, который передается в этот метод.
Например, пусть в классе пользователя будет свойство CreatedAt, которое представляет дату занесения пользователя в базу данных:
using System; public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public DateTime CreatedAt { get; set; } }
Для генерации значения этого свойства в базе данных можно вызывать функцию GETDATE():
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>() .Property(u => u.CreatedAt) .HasDefaultValueSql("GETDATE()"); } }
В метод HasDefaultValueSql()
передается SQL-выражение, которые вызывается при добавлении объекта User в базу данных.
Столбцы могут иметь значение, которое вычисляется на основании остальных столбцов. Например, пусть модель User имеет свойства для хранения имени и фамилии:
public class User { public int Id { get; set; } public string Name { get;} public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } }
А свойство Name должно представлять объединение свойств FirstName и LastName. И через Fluent API с помощью метода HasComputedColumnSql() можно установить в бд SQL-выражение, которое будет устанавливать значение столбца Name на основании столбцов FirstName и LastName:
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>() .Property(u => u.Name) .HasComputedColumnSql("[FirstName] + ' ' + [LastName]"); } }
Применение:
using (ApplicationContext db = new ApplicationContext()) { User user1 = new User() { FirstName = "Tom", LastName="Smith", Age=36 }; Console.WriteLine(user1.Name); // до добавления Name имеет значение по умолчанию db.Users.Add(user1); db.SaveChanges(); Console.WriteLine(user1.Name); // Tom Smith }