[C#] Explicitly Defining Primary Keys and Auto-Increment Behavior in Entity Framework Core

目次

Overview

In Entity Framework Core (EF Core), a property named Id is usually treated as the primary key. If the property is a numeric type, the database automatically configures it as an Identity (auto-increment) column. However, there are cases where you want to use a “meaningful string” like an employee code as a primary key or manage IDs manually within the application instead of using auto-increment. This article explains how to use the [Key] and [DatabaseGenerated] attributes to control primary key definitions and value generation rules.

Specifications

  • Input: An entity class and the property you want to set as the primary key.
  • Process: Use the [Key] attribute to mark the primary key. Use [DatabaseGenerated(DatabaseGeneratedOption.None)] to disable auto-increment.
  • Output: The specified property is set as the primary key, and the database will not generate values automatically during an INSERT operation.

Basic Usage

Use the System.ComponentModel.DataAnnotations and System.ComponentModel.DataAnnotations.Schema namespaces to apply attributes to the target property.

public class Student
{
    // Use a manually assigned Student ID as the primary key without auto-increment
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public string StudentId { get; set; }
    
    public string Name { get; set; }
}

Full Code Example

The following example demonstrates a complete console application that uses a string like an ISBN code as a primary key and registers data without auto-increment. Note: This requires the Microsoft.EntityFrameworkCore.InMemory package. dotnet add package Microsoft.EntityFrameworkCore.InMemory

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace PrimaryKeySample
{
    // 1. Entity class definition
    public class BookItem
    {
        // Use an ISBN code as the primary key
        // Strings do not auto-increment by default, but we use Option.None
        // to make the intention clear.
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public string IsbnCode { get; set; } = string.Empty;

        public string Title { get; set; } = string.Empty;

        public DateTime PublishedDate { get; set; }
    }

    // 2. DbContext definition
    public class LibraryContext : DbContext
    {
        public DbSet<BookItem> Books { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // Use an in-memory database for testing
            optionsBuilder.UseInMemoryDatabase("LibraryDb");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Saving data (assigning the ID manually)
            using (var context = new LibraryContext())
            {
                var newBook = new BookItem
                {
                    // Explicitly specify the primary key value in the application
                    IsbnCode = "978-3-16-148410-0",
                    Title = "Advanced C# Programming",
                    PublishedDate = DateTime.Today
                };

                context.Books.Add(newBook);
                context.SaveChanges();

                Console.WriteLine($"Saved: ISBN={newBook.IsbnCode}, Title={newBook.Title}");
            }

            // Loading data
            using (var context = new LibraryContext())
            {
                // The Find method efficiently searches using the primary key
                var loadedBook = context.Books.Find("978-3-16-148410-0");

                if (loadedBook != null)
                {
                    Console.WriteLine("--- Retrieved Data ---");
                    Console.WriteLine($"ISBN  : {loadedBook.IsbnCode}");
                    Console.WriteLine($"Title : {loadedBook.Title}");
                }
            }
        }
    }
}

Example Execution Result

Saved: ISBN=978-3-16-148410-0, Title=Advanced C# Programming
--- Retrieved Data ---
ISBN  : 978-3-16-148410-0
Title : Advanced C# Programming

Customization Points

Types of DatabaseGeneratedOption

  • None: The database does not generate values. The application must set the value (as shown in this example).
  • Identity: Values are generated automatically during an insert. This is the default behavior for integer primary keys.
  • Computed: The column is recalculated by the database during updates (useful for updated timestamps or calculated columns).

Using Guids

When using a Guid as a primary key, you can choose whether the database generates it (Identity) or if you generate it in C# using Guid.NewGuid() (None).

Important Notes

  • Duplicate Errors: When using DatabaseGeneratedOption.None, you are responsible for the uniqueness of the primary key. Attempting to save an existing key will cause a DbUpdateException.
  • Primary Key Modification: In EF Core, you cannot change the primary key value of an entity once it is tracked by the context. To change a key, you must delete the old record and insert a new one.
  • Composite Key Limit: The [Key] attribute only works for single-column primary keys. To create a composite primary key using multiple properties, you must use the Fluent API.

Variations (Optional)

Setting a Composite Primary Key

For tables where a combination of columns, such as “Member ID” and “Contract Year,” creates a unique key, use the OnModelCreating method in your DbContext. Attributes alone cannot define composite keys.

using Microsoft.EntityFrameworkCore;

// Entity
public class AnnualContract
{
    public int MemberId { get; set; } // Part of the primary key
    public int Year { get; set; }     // Part of the primary key
    public string PlanName { get; set; }
}

// DbContext
public class ContractContext : DbContext
{
    public DbSet<AnnualContract> Contracts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Define the combination of two properties as the primary key
        modelBuilder.Entity<AnnualContract>()
            .HasKey(c => new { c.MemberId, c.Year });
    }
}

Summary

For composite keys involving multiple columns, use the HasKey method within OnModelCreating instead of using attributes.

Use the [Key] attribute to set a primary key that does not follow the Id naming convention.

Use [DatabaseGenerated(DatabaseGeneratedOption.None)] to disable auto-increment and set values manually.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次