Как вы применяете внешние ключи (FOREIGN KEY) в SQL и ORM?

«Как вы применяете внешние ключи (FOREIGN KEY) в SQL и ORM?» — вопрос из категории Базы данных, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, постоянно использую внешние ключи как на уровне базы данных, так и в ORM (Entity Framework Core) для обеспечения целостности данных и навигации.

1. На уровне SQL (DDL): Внешний ключ гарантирует, что значение в столбце (например, CustomerID в Orders) ссылается на существующую запись в связанной таблице (Customers).

   CREATE TABLE Orders (
       OrderID INT PRIMARY KEY,
       CustomerID INT NOT NULL,
       OrderDate DATETIME,
       CONSTRAINT FK_Orders_Customers FOREIGN KEY (CustomerID)
           REFERENCES Customers(CustomerID)
           ON DELETE CASCADE   -- Удалить заказы при удалении клиента
           ON UPDATE NO ACTION -- Запретить изменение PK у клиента
   );

Поведения (ON DELETE/ON UPDATE):

  • NO ACTION / RESTRICT: Запретить операцию (по умолчанию в большинстве СУБД).
  • CASCADE: Каскадно удалить/обновить связанные записи.
  • SET NULL / SET DEFAULT: Установить в столбце NULL или значение по умолчанию.

2. В Entity Framework Core: Связи (и соответствующие внешние ключи) настраиваются через навигационные свойства.

   // Модели
   public class Customer
   {
       public int Id { get; set; }
       public string Name { get; set; }
       public ICollection<Order> Orders { get; set; } // Навигационное свойство
   }

   public class Order
   {
       public int Id { get; set; }
       public DateTime OrderDate { get; set; }

       // Внешний ключ и навигационное свойство
       public int CustomerId { get; set; }
       public Customer Customer { get; set; }
   }

   // Конфигурация в DbContext (Fluent API) - явное указание FK и поведения
   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
       modelBuilder.Entity<Order>()
           .HasOne(o => o.Customer)
           .WithMany(c => c.Orders)
           .HasForeignKey(o => o.CustomerId)
           .OnDelete(DeleteBehavior.Cascade); // Поведение при удалении
   }

Важно: Выбор DeleteBehavior в EF Core влияет на то, как будет сгенерирован SQL и обработаны зависимые сущности в памяти до отправки в БД.