🔑 Keys in EF Core

✅ 1. Primary Key (PK)

Definition: Uniquely identifies each entity. Default Convention: Property named Id or <EntityName>Id. Explicit Definition: Data Annotation: [Key]
public int ProductId { get; set; }
Fluent API: modelBuilder.Entity<Product>()
.HasKey(p => p.ProductId);

✅ 2. Composite Key

modelBuilder.Entity<OrderDetail>()
.HasKey(od => new { od.OrderId, od.ProductId });
⚠️ Must be defined using Fluent API (Data Annotations don’t support composite keys).

✅ 3. Alternate Key

modelBuilder.Entity<Product>()
.HasAlternateKey(p => p.Sku);

✅ Primary Key vs Alternate Key

Feature Primary Key Alternate Key
Uniquely identifies? ✅ Yes ✅ Yes
Required? ✅ Yes ❌ Optional
Can be used as FK? ✅ Yes ✅ Yes (with .HasPrincipalKey)

✅ 4. Foreign Key (FK)

modelBuilder.Entity<Employee>()
.HasOne(e => e.Department)
.WithMany(d => d.Employees)
.HasForeignKey(e => e.DepartmentId);

✅ 5. Composite Foreign Key

modelBuilder.Entity<OrderDetail>()
.HasOne(od => od.Product)
.WithMany()
.HasForeignKey(od => new { od.ProductId, od.SupplierId });

✅ 6. Shadow Properties & Shadow Foreign Keys

Shadow Property: modelBuilder.Entity<Order>()
.Property<int>("ShadowCustomerId");
Shadow FK: modelBuilder.Entity<Order>()
.HasOne(typeof(Customer))
.WithMany()
.HasForeignKey("ShadowCustomerId");

✅ 7. Alternate Key as Principal in FK

modelBuilder.Entity<Product>()
.HasAlternateKey(p => p.Sku);

modelBuilder.Entity<OrderDetail>()
.HasOne(od => od.Product)
.WithMany()
.HasForeignKey(od => od.ProductSku)
.HasPrincipalKey(p => p.Sku);

✅ 8. Keyless Entities

modelBuilder.Entity<MyView>().HasNoKey();
  • Cannot be updated, inserted, or deleted.
  • Cannot participate in relationships.

✅ 9. Unique Constraints Across Multiple Columns

modelBuilder.Entity<User>()
.HasAlternateKey(u => new { u.Email, u.Username });
Or using Index: modelBuilder.Entity<User>()
.HasIndex(u => u.Email)
.IsUnique();
👉 .HasIndex() creates an index (performance), .HasAlternateKey() enforces uniqueness in EF model.

✅ 10. Common Errors and Gotchas

SituationOutcome/Error
No PK defined❌ Runtime error
Changing PK in migration❌ Drops table → risk of data loss
Nullable FK with .OnDelete(SetNull)❌ Error if FK is non-nullable
FK type mismatch❌ Runtime exception
Optional FK in composite key❌ Not allowed
Required navigation with optional FK❌ Invalid – must match nullability

✅ 11. Key Usage in Change Tracking

For composite keys, all values must match for EF Core to treat two entities as the same in the ChangeTracker.

✅ 12. Fluent API vs Data Annotations

FeatureFluent APIData Annotations
Flexibility✅ High❌ Limited
Composite Keys✅ Yes❌ Not supported
Precedence✅ Overrides Data Annotations❌ Can be overridden

✅ 13. GUID as Primary Key

  • Pros: Globally unique, useful in distributed systems.
  • Cons: Poor for clustered indexes (fragmentation). Use NEWSEQUENTIALID() for better performance.
modelBuilder.Entity<User>()
.Property(u => u.UserId)
.HasDefaultValueSql("NEWSEQUENTIALID()");

✅ 14. Renaming Foreign Key Constraint

modelBuilder.Entity<Employee>()
.HasOne(e => e.Department)
.WithMany()
.HasForeignKey(e => e.DepartmentId)
.HasConstraintName("FK_Employee_Department");

✅ 15. Default Key Generation Strategies

modelBuilder.Entity<Product>()
.Property(p => p.ProductId)
.ValueGeneratedOnAdd();
  • .ValueGeneratedOnAdd() → Auto-increment/identity.
  • .ValueGeneratedNever() → Must be set manually.
  • .ValueGeneratedOnAddOrUpdate() → For computed columns.

✅ 16. Owned Entities and Keys

modelBuilder.Entity<Order>()
.OwnsOne(o => o.ShippingAddress);

Owned types share the same PK as the owner.

✅ 17. Summary Table

ConceptDescriptionExample
Primary KeyUnique identity.HasKey(x => x.Id)
Composite KeyMultiple properties as PK.HasKey(x => new { x.A, x.B })
Alternate KeyAdditional unique constraint.HasAlternateKey(x => x.Email)
Foreign KeyConnects entities.HasForeignKey()
Shadow PropertyNot in class but in EF model.Property<string>("Code")
Shadow FKFK property not in class.HasForeignKey("CustomerId")
Keyless EntityNo primary key.HasNoKey()
Rename FKCustom FK name.HasConstraintName("FK_Custom")
GUID as PKUnique but fragmented indexUse NEWSEQUENTIALID()
Default Key StrategyConfigure key generation.ValueGeneratedOnAdd()
Owned Entity KeyShares owner’s PK.OwnsOne(...)

✅ 18. Final Tip

👉 Best Practice: Define keys clearly in your model from the beginning. Avoid changing PK/AK definitions after production to prevent migration issues and data loss.

Comments

Popular posts from this blog

Logging in .NET Core: Built-in Logging vs Serilog with Full Implementation Guide

Factory Method Design Pattern in .NET — Real-Time Finance Example

Implementing Single Sign-On (SSO) in .NET Core and Angular