Question
Answers how to model a single users table with role-based vehicle relationships in EF Core, comparing three design options.
What this file does
Answers how to model a single users table with role-based vehicle relationships in EF Core, comparing three design options.
When to use it
- Designing a database with a single users table for multiple roles
- Needing EF Core navigation properties that depend on user role
- Deciding between conditional loading, backing fields, or table splitting
- Enforcing that only drivers have a vehicles relationship
Assumes this stack
Question
I have a concern like this: in the database, there will be no distinct tables for passengers, drivers or managers, but only a users table. therefore there should only be User entity (EF Core) to map to the table. however, the users table has one-to-many relationship with vehicles, and this should only apply to users that have role driver (Role is an entity, representing the roles table). How should I handle this?
Answer
This is a common issue in EF Core when using a single "Users" table for multiple roles while enforcing role-based constraints . Since only drivers should have a vehicles relationship, you have a few design options:
Option 1: Use Navigation Property with Conditional Loading
You keep a single User entity , but filter vehicle access based on role .
Implementation
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Role Role { get; set; } // Navigation property to Role
public int RoleId { get; set; } // Foreign Key
// Navigation property: Only relevant for Drivers
public List<Vehicle>? Vehicles { get; set; }
}
public class Vehicle
{
public int Id { get; set; }
public string LicensePlate { get; set; }
public int UserId { get; set; } // FK to User
public User User { get; set; } // Navigation property
}
Enforcing the Role Restriction
You filter access to vehicles in queries (prevent non-drivers from having vehicles):
var driversWithVehicles = dbContext.Users
.Where(u => u.Role.Name == "Driver")
.Include(u => u.Vehicles)
.ToList();
📌 Pros : Keeps database simple, avoids unnecessary table inheritance.
📌 Cons : Need to manually enforce role-based constraints in logic.
Option 2: Use Backing Fields with Explicit Role Checks
Instead of exposing Vehicles to all users, use a private backing field to ensure only drivers have access.
Implementation
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Role Role { get; set; }
public int RoleId { get; set; }
// Private field (EF Core still maps this correctly)
private List<Vehicle>? _vehicles;
// Public property ensures only Drivers can access
public IReadOnlyList<Vehicle>? Vehicles
=> Role?.Name == "Driver" ? _vehicles : null;
}
📌 Pros : Prevents incorrect usage at the code level.
📌 Cons : Requires manual enforcement at object level (EF Core does not enforce this automatically).
Option 3: Table Splitting (Single Table with Different Mappings)
EF Core table splitting allows mapping a subset of properties only when a condition is met .
Implementation
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Role Role { get; set; }
}
public class Driver
{
public int Id { get; set; } // Same as User Id
public List<Vehicle> Vehicles { get; set; }
}
EF Core Fluent API Mapping
modelBuilder.Entity<User>()
.ToTable("Users");
modelBuilder.Entity<Driver>()
.ToTable("Users") // Same table
.HasOne<Role>()
.WithMany()
.HasForeignKey(u => u.Id)
.HasPrincipalKey(u => u.Id);
📌 Pros : EF Core ensures only drivers have vehicles automatically.
📌 Cons : More complex EF Core configuration.
Which Approach is Best?
| Approach | Simplicity | Enforces Role Constraint | Works Well with EF Core |
|---|---|---|---|
| 1: Conditional Loading | ✅ Simple | ❌ Manual enforcement | ✅ Good for performance |
| 2: Backing Fields | ✅ Simple | ✅ Enforces constraint | ✅ Works well |
| 3: Table Splitting | ❌ Complex | ✅ Automatic enforcement | ✅ EF Core manages well |
📌 Recommendation
- If you want a simple solution → Use Option 1 (Conditional Loading)
- If you want stricter role constraints → Use Option 2 (Backing Fields)
- If EF Core should enforce the structure automatically → Use Option 3 (Table Splitting)
Do you want help implementing one of these fully in Fluent API ? 🚀
References
What's inside
3 design options with code examples, a comparison table, and a recommendation section.
Change this for your project
- Replace
Role.Name == "Driver"with your actual role name - Replace
dbContext.Userswith your own DbContext instance name
Where it goes
Keep it in your repository where the agent or team that needs it will read it.
Worth borrowing
- Using a private backing field with a role-checked public property to enforce constraints at the code level
- Table splitting to map two entities to the same table for automatic role-based mapping
Related Documents
DunApp PWA - Project Constraints
Defines 14 hard constraints for a Hungarian PWA project, banning Netlify deployment and enforcing local-only testing, Supabase backend, and zero-cost development.
Constraints
Defines a three-tier priority system for design decisions, with conflict resolution examples to guide trade-offs.
Version Constraints Guide
Teaches Composer version constraint syntax for WordPress plugins and themes using a custom shell script wrapper.
Specifying version constraints
Explains how to pin Terraform CLI, provider, and Ansible versions for IBM Cloud Schematics workspaces and actions.