Introduced in C# 12, primary constructors allow you to declare constructor parameters directly on the class or struct declaration, significantly reducing boilerplate code.

Syntax Comparison

Primary Constructor (Modern C# 12+)

public class GameStoreContext(DbContextOptions<GameStoreContext> options) : DbContext(options)
{
    // Class members can directly use 'options' here
}

Classical Equivalent

public class GameStoreContext : DbContext
{
    public GameStoreContext(DbContextOptions<GameStoreContext> options) : base(options)
    {
    }
}

Key Characteristics

  • Scope: Parameters defined in the primary constructor header are in scope for the entire class body.

  • Field Capture (Classes vs. Records):

    • In record types, primary constructor parameters automatically become public init-only properties.

    • In standard class types, they remain parameters (not properties), but the compiler will automatically capture them into private backing fields if they are accessed within methods or properties inside the class.

  • Base Class Chaining: You can pass parameters directly to a base class constructor using syntax like : BaseClass(paramName).

    public class Person(string name)
    {
        public string Name { get; } = name;
    }
     
    public class Employee(string name, string employeeId) : Person(name)
    {
        public string EmployeeId { get; } = employeeId;
    }

In this example, the Employee class uses a primary constructor to accept both name and employeeId. It immediately forwards the name parameter up to the base Person class constructor using : Person(name), while retaining employeeId for its own use.

Common EF Core Usage This pattern is widely adopted in Entity Framework Core for clean, minimal DbContext declarations without redundant constructor bodies.