Keywords: Entity Framework | Primary Key Auto-generation | DatabaseGenerated | Value Generation Strategy | EF Core Configuration
Abstract: This technical article provides an in-depth analysis of primary key auto-generation mechanisms in Entity Framework. Through practical case studies, it explains why string-type primary keys cause insertion failures and demonstrates proper configuration using int-type keys. The article covers DatabaseGenerated annotations, value generation strategies, and includes comprehensive code examples for effective EF Core implementation.
Problem Context and Phenomenon Analysis
During Entity Framework Core application development, developers frequently encounter primary key generation failures when inserting entities. A typical scenario involves creating user entities through Web API with JSON data, where the system throws a "Cannot insert the value NULL into column 'Id'" exception despite explicitly annotating the entity class with [DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute.
In the specific case, the user entity is defined as follows:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
public string Username { get; set; }
// Other properties...
}When the client sends a JSON request without the Id field:
{Username:"Bob", FirstName:"Foo", LastName:"Bar", Password:"123", Headline:"Tuna"}EF Core cannot automatically generate the primary key value, causing the database insertion operation to fail. However, if an Id value is explicitly provided in the request, the operation executes successfully.
Root Cause Investigation
The core issue lies in the matching between primary key data type selection and EF Core value generation strategy. According to EF Core official documentation, the value generation mechanism has different default behaviors for different data types:
- For numeric primary keys like
short,int,long, EF Core defaults to configuring automatic value generation on insert - For
Guidprimary keys, some database providers support client-side value generation - For
stringprimary keys, automatic value generation is typically not configured by default
In SQL Server databases, numeric primary keys usually map to IDENTITY columns, which support auto-increment functionality at the database level. String-type primary keys cannot directly map to auto-increment columns and require additional configuration for automatic value generation.
Solution Implementation
Based on problem analysis, the most direct solution is changing the primary key type from string to int:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string Headline { get; set; }
// Navigation properties remain unchanged
}After modification, EF Core automatically configures the Id property as an auto-incrementing primary key in the database, requiring no additional data annotations. In the controller, the same insertion code now works correctly:
[HttpPost]
public async Task<ActionResult<User>> PostUser(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}Advanced Configuration Options
For special scenarios requiring string primary keys, EF Core provides multiple value generation configuration methods:
Default Value Configuration
Configure property default values via Fluent API:
modelBuilder.Entity<User>()
.Property(u => u.Id)
.HasDefaultValueSql("NEWID()"); // For GUID generation in SQL ServerComputed Column Configuration
For scenarios requiring values computed from other columns:
modelBuilder.Entity<Person>()
.Property(p => p.DisplayName)
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");Explicit Value Generation Control
Explicitly configure value generation using the ValueGeneratedOnAdd method:
modelBuilder.Entity<User>()
.Property(u => u.Id)
.ValueGeneratedOnAdd();Associated Entity Synchronization
After modifying the primary key type, ensure all foreign key references in associated entities are synchronized. For example, change the UserId in the Connection entity to int type as well:
public class Connection
{
public string ConnectionId { get; set; }
public int UserId { get; set; } // Synchronously changed to int type
public virtual User User { get; set; }
}Best Practice Recommendations
- Primary Key Type Selection: Prefer numeric types (int, long) as primary keys for optimal automatic generation support
- Naming Conventions: Follow EF Core naming conventions, such as using
IdorEntityName+Idas primary key property names - Configuration Clarity: In complex scenarios, use Fluent API to explicitly configure value generation strategies, avoiding reliance on implicit conventions
- Testing Validation: After modifying primary key configuration, thoroughly test all related CRUD operations to ensure data consistency
Conclusion
Entity Framework Core's value generation mechanism relies on close coordination between data types and database features. String-type primary keys typically cannot achieve out-of-the-box auto-increment functionality due to database limitations. By changing primary key types to numeric, developers can fully leverage EF Core's automatic value generation features, simplifying data persistence logic and improving development efficiency. For special requirements necessitating string primary keys, explicit configuration is required to implement appropriate value generation strategies.